Segurança de aplicações TypeScript: top 10 vulnerabilidades
TypeScript, como uma extensão do JavaScript, herda muitas de suas vulnerabilidades, adicionando riscos específicos relacionados ao sistema de tipos e à compilação. Mesmo a tipagem estrita não garante segurança — erros de lógica e práticas inseguras podem levar a consequências graves. Neste artigo, analisaremos as top 10 vulnerabilidades de aplicações TypeScript, forneceremos exemplos de código e daremos recomendações de proteção.
1. Cross-Site Scripting (XSS) via dangerouslySetInnerHTML
Uma das vulnerabilidades mais comuns em aplicações React/TypeScript é a inserção de HTML sem escape através de dangerouslySetInnerHTML.
// ❌ Código vulnerávelfunction UserComment({ comment }: { comment: string }) { return <div dangerouslySetInnerHTML={{ __html: comment }} />;}
// ✅ Versão seguraimport DOMPurify from 'dompurify';
function SafeComment({ comment }: { comment: string }) { const sanitized = DOMPurify.sanitize(comment); return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;}2. Injeção de SQL via consultas sem escape
Usar template strings em consultas SQL com entrada do usuário é um caminho direto para injeção.
// ❌ Código vulnerávelasync function getUser(id: string) { const query = `SELECT * FROM users WHERE id = '${id}'`; return db.execute(query);}
// ✅ Versão segura (uso de consultas parametrizadas)async function safeGetUser(id: string) { const query = 'SELECT * FROM users WHERE id = $1'; return db.execute(query, [id]);}3. Prototype Pollution via mesclagem profunda de objetos
TypeScript permite copiar objetos facilmente, mas não verifica a cadeia de protótipos.
// ❌ Código vulnerávelfunction merge(target: any, source: 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;}
// ✅ Versão segurafunction safeMerge(target: Record<string, any>, source: Record<string, any>) { const result = { ...target }; for (const key of Object.keys(source)) { if (key === '__proto__' || key === 'constructor') continue; if (typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = safeMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } return result;}4. Injeção NoSQL (MongoDB)
Ao trabalhar com MongoDB, passar um objeto diretamente da requisição pode levar à injeção de operadores $where ou $regex.
// ❌ Código vulnerávelapp.post('/login', async (req, res) => { const { username, password } = req.body; const user = await User.findOne({ username, password }); // Se passar { "$gt": "" }, é possível contornar a verificação});
// ✅ Versão seguraimport { sanitize } from 'mongo-sanitize';
app.post('/login', async (req, res) => { const cleanBody = sanitize(req.body); const user = await User.findOne({ username: cleanBody.username, password: cleanBody.password });});5. Comandos do SO via child_process
Executar entrada do usuário através de exec sem escape é uma vulnerabilidade crítica.
// ❌ Código vulnerávelimport { exec } from 'child_process';
function runCommand(input: string) { exec(`echo ${input}`, (err, stdout) => { console.log(stdout); });}
// ✅ Versão seguraimport { execFile } from 'child_process';
function safeCommand(input: string) { // Usamos execFile com um array de argumentos execFile('echo', [input], (err, stdout) => { console.log(stdout); });}