Segurança de aplicações TypeScript: top 10 vulnerabilidades na prática
TypeScript é uma extensão do JavaScript que adiciona tipagem estática. Muitos desenvolvedores acreditam erroneamente que a tipagem rigorosa torna automaticamente a aplicação segura. Isso não é verdade. O compilador TypeScript verifica tipos, mas não protege contra erros lógicos, injeções ou problemas de autenticação. Neste artigo, analisaremos as 10 vulnerabilidades mais comuns em aplicações TypeScript, mostraremos exemplos de código e daremos recomendações específicas de proteção. O artigo será útil tanto para desenvolvedores iniciantes quanto para experientes.
1. Cross-Site Scripting (XSS) através de tratamento incorreto de entrada
XSS continua sendo uma das vulnerabilidades mais perigosas. TypeScript não escapa a saída automaticamente. Se você inserir dados do usuário em um template HTML sem sanitização, estará abrindo a porta para ataques.
Código vulnerável
function renderComment(comment: string): string { return `<div>${comment}</div>`;}
// O invasor envia: <script>alert('XSS')</script>Código corrigido
import { escape } from 'lodash';
function renderComment(comment: string): string { const safeComment = escape(comment); return `<div>${safeComment}</div>`;}Recomendação: Sempre use bibliotecas para escape (DOMPurify, lodash.escape) ou mecanismos de template com auto-escape (React JSX, Handlebars).
2. Injeções SQL através de templates de string
TypeScript permite o uso de template strings para construir consultas SQL. Este é um caminho direto para injeção SQL se você inserir dados do usuário.
Código vulnerável
async function getUser(id: string): Promise<User | null> { const query = `SELECT * FROM users WHERE id = '${id}'`; const result = await db.query(query); return result.rows[0];}Código corrigido
async function getUser(id: string): Promise<User | null> { const query = 'SELECT * FROM users WHERE id = $1'; const result = await db.query(query, [id]); return result.rows[0];}Recomendação: Use consultas parametrizadas ou ORM (TypeORM, Prisma, Sequelize). Nunca concatene strings para SQL.
3. Comparação insegura de senhas
Um erro na implementação da comparação de senhas pode levar a ataques de timing. Desenvolvedores TypeScript frequentemente usam o operador === para comparar hashes, o que é inseguro.
Código vulnerável
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { const inputHash = await bcrypt.hash(inputPassword, 10); return inputHash === storedHash; // Vulnerabilidade: timing attack}Código corrigido
import bcrypt from 'bcrypt';
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { return bcrypt.compare(inputPassword, storedHash);}Recomendação: Use funções de comparação nativas de bibliotecas de hash (bcrypt.compare, argon2.verify). Elas são protegidas contra ataques de timing.
4. Prototype Pollution através de mesclagem recursiva de objetos
Aplicações TypeScript frequentemente usam funções de cópia profunda ou mesclagem de objetos. Se as chaves não forem controladas, um invasor pode poluir o protótipo Object.prototype.
Código vulnerável
function merge(target: Record<string, any>, source: Record<string, any>) { for (const key in source) { if (typeof source[key] === 'object') { target[key] = merge(target[key] || {}, source[key]); } else { target[key] = source[key]; } } return target;}
// O invasor en