Introdução: por que a segurança de aplicações JavaScript não é uma opção, mas uma necessidade
JavaScript (especialmente na plataforma Node.js) tornou-se a linguagem dominante para criar aplicações tanto do lado do cliente quanto do servidor. No entanto, com o aumento da popularidade, cresce também o número de ataques. De acordo com a OWASP, a maioria das invasões ocorre devido a erros típicos que podem ser prevenidos. Neste artigo, analisaremos o top 10 vulnerabilidades JavaScript (Node.js) com exemplos reais de código e métodos de proteção. O material será útil tanto para desenvolvedores iniciantes quanto experientes que desejam fortalecer a segurança de seus projetos.
1. Injeções (SQL, NoSQL, Command Injection)
As injeções continuam sendo a categoria mais perigosa de vulnerabilidades. No Node.js, MongoDB e PostgreSQL são frequentemente afetados.
Exemplo de código vulnerável (Injeção NoSQL):
// Código vulnerávelapp.post('/login', async (req, res) => { const { username, password } = req.body; const user = await db.collection('users').findOne({ username: username, password: password }); // Se passar { "username": { "$ne": null }, "password": { "$ne": null } }, retornará o primeiro usuário});Como corrigir:
- Use ORM/ODM com parametrização (Mongoose, Sequelize).
- Valide e sanitize os dados de entrada (biblioteca
validatoroujoi). - Para comandos, use
execFileem vez deexec.
2. Cross-Site Scripting (XSS)
XSS permite que um invasor injete um script malicioso na página visualizada por outros usuários.
Exemplo de código vulnerável (XSS Refletido):
// Rota Express vulnerávelapp.get('/search', (req, res) => { const query = req.query.q; res.send(`<h1>Resultados da pesquisa para: ${query}</h1>`); // Se passar ?q=<script>alert('XSS')</script>, o script será executado});Proteção:
- Escape a saída: use
res.rendercom mecanismos de template (EJS, Pug) ouencodeURIComponent. - Defina o cabeçalho HTTP
Content-Security-Policy. - Não use
innerHTMLno cliente — utilizetextContent.
3. Autenticação e gerenciamento de sessões inseguros
Autenticação fraca é uma das principais causas de vazamento de dados. Frequentemente, desenvolvedores codificam chaves diretamente no código ou usam senhas fracas.
Erros típicos:
- Armazenar senhas em texto puro (em vez de bcrypt/argon2).
- Usar segredos JWT previsíveis (
secret123). - Ausência de limitação no número de tentativas de login.
Exemplo de implementação correta:
const bcrypt = require('bcrypt');const saltRounds = 12;
app.post('/register', async (req, res) => { const hash = await bcrypt.hash(req.body.password, saltRounds); // Salvar o hash no BD});
app.post('/login', async (req, res) => { const user = await db.getUser(req.body.username); const match = await bcrypt.compare(req.body.password, user.passwordHash); if (match) { // Criar JWT com curto tempo de expiração const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: '15m' }); res.json({ token }); }});4. SSRF (Server-Side Request Forgery)
A vulnerabilidade SSRF permite que um atacante force o servidor a fazer requisições a recursos internos (por exemplo, metadados do provedor de nuvem).
Exemplo:
// Código vulnerável: carregar avatar por URLapp.post('/avatar', async (req, res) => { const { url } = req.body; const response = await fetch(url); // Pode passar http://169.254.169.254/latest/meta-data/ // Processar resposta});Proteção:
- Crie uma lista de permissões de domínios autorizados.
- Bloq