Segurança de aplicações JavaScript: top 10 vulnerabilidades
Aplicações JavaScript modernas em Node.js processam enormes volumes de dados sensíveis, o que as torna um alvo atraente para invasores. De acordo com o OWASP, a maioria das invasões ocorre devido a erros típicos que podem ser prevenidos. Neste artigo, analisaremos as 10 vulnerabilidades mais comuns, forneceremos exemplos reais de código e mostraremos como proteger sua aplicação.
1. XSS (Cross-Site Scripting)
Cross-site scripting é uma das ameaças mais antigas, mas ainda atual. O invasor insere um script malicioso em uma página web, que é executado no navegador da vítima.
// Código vulnerávelapp.get('/search', (req, res) => { const query = req.query.q; res.send(`<h2>Resultados da busca para: ${query}</h2>`);});
// Versão seguraconst escapeHtml = (str) => { return str.replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, ''');};
app.get('/search', (req, res) => { const query = escapeHtml(req.query.q || ''); res.send(`<h2>Resultados da busca para: ${query}</h2>`);});2. SQL Injection
A injeção de SQL ocorre quando a entrada do usuário é concatenada diretamente em uma consulta SQL. Isso pode levar ao vazamento ou exclusão de dados.
// Código vulnerávelconst userId = req.body.id;const query = `SELECT * FROM users WHERE id = ${userId}`;
// Versão segura com consultas parametrizadasconst userId = req.body.id;const query = 'SELECT * FROM users WHERE id = ?';db.query(query, [userId], (err, results) => { // processamento dos resultados});3. Desserialização insegura
Node.js frequentemente usa JSON.parse(), mas se você trabalha com outros formatos (como Buffer), um invasor pode inserir dados maliciosos.
// Abordagem perigosaconst data = JSON.parse(req.body.data); // pode conter __proto__
// Abordagem seguraconst safeParse = (json) => { const data = JSON.parse(json); // Remove recursivamente propriedades perigosas return JSON.parse(JSON.stringify(data));};4. CSRF (Cross-Site Request Forgery)
Ataque em que o invasor força o usuário a executar uma ação indesejada em um site onde ele está autenticado.
// Configuração de proteção com csurfconst csrf = require('csurf');const csrfProtection = csrf({ cookie: true });
app.post('/transfer', csrfProtection, (req, res) => { // processamento seguro da transferência});5. Autenticação insegura
Senhas fracas, ausência de limitação de tentativas de login e armazenamento de senhas em texto puro são problemas comuns.
// Hash correto de senhasconst bcrypt = require('bcrypt');
async function createUser(password) { const salt = await bcrypt.genSalt(12); const hash = await bcrypt.hash(password, salt); // salvar hash no BD}
async function checkPassword(plainPassword, hash) { return await bcrypt.compare(plainPassword, hash);}6. Vazamento de dados confidenciais
Enviar o stack trace de erros ao cliente ou registrar senhas é um erro comum de desenvolvedores.
// Incorretoapp.use((err, req, res, next) => { res.status(500).json({ error: err.stack });});
// Corretoapp.use((err, req, res, next) => { console.error(err); // registrar no servidor res.status(500).json({ error: 'Erro interno do servidor' });});7. Configuração insegura
Uso de portas padrão, modo de depuração ativado em produção e políticas CORS fracas.
// Configuração segura de CORSconst cors = require('cors');cons