TypeScript

Dominando Segurança em TypeScript: Tipos que Previnem Vulnerabilidades Comuns em Projetos Reais

9 min de leitura

Dominando Segurança em TypeScript: Tipos que Previnem Vulnerabilidades Comuns em Projetos Reais

O Poder do Sistema de Tipos do TypeScript na Prevenção de Vulnerabilidades O TypeScript não é apenas uma ferramenta para adicionar tipos estáticos ao JavaScript — é um escudo preventivo contra uma classe inteira de vulnerabilidades de segurança. Quando você trabalha com tipos explícitos, força comportamentos seguros e elimina superfícies de ataque que existem naturalmente em código dinamicamente tipado. A verdade é que muitas vulnerabilidades comuns nascem da falta de clareza sobre o tipo e a forma dos dados que circulam pelo seu código. Neste artigo, exploraremos como aproveitar o sistema de tipos do TypeScript para construir aplicações mais seguras. Não vamos apenas escrever código que "funciona" — vamos escrever código que não pode falhar da forma que esperamos. A diferença é fundamental: no primeiro caso, confiamos em testes; no segundo, confiamos na linguagem. Tipos Primitivos e Validação de Entrada Compreendendo o Risco Real A validação de entrada é a primeira linha de defesa contra injeção de código, XSS, SQL

<h2>O Poder do Sistema de Tipos do TypeScript na Prevenção de Vulnerabilidades</h2>

<p>O TypeScript não é apenas uma ferramenta para adicionar tipos estáticos ao JavaScript — é um escudo preventivo contra uma classe inteira de vulnerabilidades de segurança. Quando você trabalha com tipos explícitos, força comportamentos seguros e elimina superfícies de ataque que existem naturalmente em código dinamicamente tipado. A verdade é que muitas vulnerabilidades comuns nascem da falta de clareza sobre o tipo e a forma dos dados que circulam pelo seu código.</p>

<p>Neste artigo, exploraremos como aproveitar o sistema de tipos do TypeScript para construir aplicações mais seguras. Não vamos apenas escrever código que &quot;funciona&quot; — vamos escrever código que <em>não pode falhar</em> da forma que esperamos. A diferença é fundamental: no primeiro caso, confiamos em testes; no segundo, confiamos na linguagem.</p>

<h2>Tipos Primitivos e Validação de Entrada</h2>

<h3>Compreendendo o Risco Real</h3>

<p>A validação de entrada é a primeira linha de defesa contra injeção de código, XSS, SQL injection e outros ataques. Em JavaScript puro, você nunca sabe realmente o tipo de um valor até tentar usá-lo. Em TypeScript, você <em>sabe</em>, desde o momento em que o código é escrito.</p>

<p>Considere um exemplo clássico: um endpoint que recebe dados de um usuário. Sem tipos, qualquer coisa entra. Com tipos, apenas o que você definir entra — e o compilador garante isso.</p>

<pre><code class="language-typescript"></code></pre>

<p>A diferença é sutil no código, mas profunda na segurança. O TypeScript verifica em tempo de compilação se você está passando os tipos corretos. Um atacante não pode forçar um string para dentro de um campo <code>number</code> — o servidor simplesmente rejeitará a requisição antes de qualquer lógica de negócio executar.</p>

<h3>Literal Types para Enumerações Seguras</h3>

<p>Quando você tem um conjunto finito e bem-definido de valores (como status de pedidos ou níveis de acesso), use literal types. Isso previne que valores inesperados circulem pelo seu código.</p>

<pre><code class="language-typescript"></code></pre>

<p>Literal types transformam valores em contratos. Não há negociação — ou você passa um valor válido, ou o código não executa.</p>

<h2>Tipos Opcionais e Null Safety</h2>

<h3>O Problema do Null Coalescing</h3>

<p>Um dos bugs mais comuns em produção é tentar acessar propriedades de <code>null</code> ou <code>undefined</code>. Em JavaScript, você só descobre em runtime. Em TypeScript com <code>strictNullChecks</code>, você descobre na compilação.</p>

<pre><code class="language-typescript"></code></pre>

<p>Ativar <code>strictNullChecks</code> no seu <code>tsconfig.json</code> é uma das mudanças mais impactantes que você pode fazer. Força seu time a lidar explicitamente com valores que podem ser ausentes, eliminando uma classe inteira de bugs.</p>

<h3>Non-null Assertion e seus Perigos</h3>

<p>Existe a tentation de usar <code>!</code> para &quot;desabilitar&quot; a verificação null. Evite isso quando possível — é uma abertura intencional em seu escudo de segurança.</p>

<pre><code class="language-typescript">// ⚠️ Usar com extrema cautela

interface Dados {

valor?: string;

}

const dados: Dados = obterDadosDoBancoDados();

// Isto compila porque você &quot;prometeu&quot; ao TypeScript que valor existe

const resultado = dados.valor!.toUpperCase();

// Mas se valor for undefined, você terá um erro em runtime

// Use apenas quando tiver 100% de certeza</code></pre>

<p>A única exceção legítima é quando você tem conhecimento de domínio que o TypeScript não consegue expressar — e mesmo assim, comente seu código explicando <em>por que</em> você sabe que é seguro.</p>

<h2>Tipos Genéricos e Sanitização de Dados</h2>

<h3>Criando Wrappers de Tipo para Dados Perigosos</h3>

<p>Em uma aplicação real, você trabalha com dados de múltiplas origens: APIs externas, formulários, cookies, headers HTTP. Nem todos são confiáveis. Você pode usar tipos genéricos para criar &quot;wrappers&quot; que explicitam quando um dado foi validado e quando não.</p>

<pre><code class="language-typescript"></code></pre>

<p>Este padrão força refatoração em todos os fluxos de dados. Qualquer valor que chegue da internet deve ser explicitamente validado antes de ser usado em operações sensíveis. Não é mágica — é discipline codificada no sistema de tipos.</p>

<h3>Tipos Distributivos para Sanitização</h3>

<p>Para casos onde você precisa sanitizar propriedades específicas de um objeto, tipos distributivos são poderosos:</p>

<pre><code class="language-typescript">// Remove campos sensíveis após sanitização

type SensitiveFields = &quot;senha&quot; | &quot;token_api&quot; | &quot;cpf&quot;;

type Sanitized&lt;T&gt; = {

[K in keyof T]: K extends SensitiveFields ? never : T[K];

};

interface Usuario {

id: number;

nome: string;

email: string;

senha: string;

token_api: string;

}

function retornarParaFrontend(usuario: Usuario): Sanitized&lt;Usuario&gt; {

// TypeScript força você a remover os campos sensíveis

const { senha, token_api, ...seguro } = usuario;

return seguro as Sanitized&lt;Usuario&gt;;

}

// Se você tentar incluir um campo sensível, falha:

// return { ...usuario } as Sanitized&lt;Usuario&gt;; // ❌ Erro</code></pre>

<h2>Prevenção de Ataques Comuns Através de Tipos</h2>

<h3>CSRF e Validação de Estado</h3>

<p>Ataques CSRF funcionam porque um navegador enviará automaticamente cookies com qualquer requisição. Você pode usar o sistema de tipos para garantir que certas operações sensíveis sempre passam por validações específicas.</p>

<pre><code class="language-typescript"></code></pre>

<h3>XSS Prevention com Tipos de String Especializados</h3>

<p>Para aplicações que renderizam HTML no servidor ou no cliente, você pode criar tipos que marcam strings como &quot;seguras&quot; (já escapadas).</p>

<pre><code class="language-typescript"></code></pre>

<p>O compilador não deixará você renderizar HTML não-escapado. Ponto.</p>

<h2>Conclusão</h2>

<p>Aprendemos três lições fundamentais neste artigo. Primeiro: <strong>tipos explícitos são prevenção de bugs</strong>. Quando você define tipos rigorosos, elimina superfícies de ataque antes do código ser executado. O TypeScript verifica em compilação o que levaria horas de testes e debugging em JavaScript puro.</p>

<p>Segundo: <strong>null safety e validação de entrada são não-negociáveis</strong>. <code>strictNullChecks</code> força você a lidar com valores ausentes, e tipos genéricos como <code>Untrusted&lt;T&gt;</code> e <code>Trusted&lt;T&gt;</code> criam contratos que obrigam validação em pontos críticos. Não é burocracia — é segurança.</p>

<p>Terceiro: <strong>tipos especializados (branded types) codificam conhecimento de segurança</strong>. Em vez de confiar em comentários ou documentação, você força comportamentos seguros através do compilador. Um <code>CSRFToken</code> é diferente de uma <code>string</code> comum; HTML escapado é diferente de HTML bruto. Seu código se torna uma máquina de estados tipada que naturalmente segue as melhores práticas de segurança.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Strict Null Checks</a></li>

<li><a href="https://owasp.org/www-project-top-ten/" target="_blank" rel="noopener noreferrer">OWASP: Top 10 Web Application Security Risks</a></li>

<li><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/" target="_blank" rel="noopener noreferrer">TypeScript 5.0 Release Notes - Security Features</a></li>

<li><a href="https://egghead.io/articles/using-branded-types-in-typescript" target="_blank" rel="noopener noreferrer">Brand Types in TypeScript - Egghead.io</a></li>

<li><a href="https://learn.microsoft.com/en-us/windows/security/threat-protection/secure-coding-guidelines" target="_blank" rel="noopener noreferrer">Secure Coding Guidelines - Microsoft Security Development Lifecycle</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em TypeScript

Boas Práticas de Escrevendo Arquivos .d.ts: Tipando Bibliotecas JavaScript Existentes para Times Ágeis
Boas Práticas de Escrevendo Arquivos .d.ts: Tipando Bibliotecas JavaScript Existentes para Times Ágeis

O que são Arquivos .d.ts e Por Que Importam Os arquivos (TypeScript Declarati...

O que Todo Dev Deve Saber sobre NestJS com TypeScript: Arquitetura Modular e Injeção de Dependências
O que Todo Dev Deve Saber sobre NestJS com TypeScript: Arquitetura Modular e Injeção de Dependências

O que é NestJS e por que arquitetura modular importa NestJS é um framework pr...

Guia Completo de Tipos Primitivos, Literais e Type Inference em TypeScript
Guia Completo de Tipos Primitivos, Literais e Type Inference em TypeScript

Tipos Primitivos em TypeScript Os tipos primitivos são a base de qualquer pro...