Protegendo suas APIs Node.js: O Básico Que Quase Ninguém Faz (Mas Deveria)

Você pode estar escrevendo código limpo, usando boas práticas de arquitetura e entregando features novas toda semana… mas se sua API não estiver segura, você só está acelerando em direção ao desastre.
Segurança não é coisa só de DevOps, especialista ou projeto enterprise. É base. É fundação. E o mais insano? A maior parte das APIs que estão em produção hoje negligenciam o mínimo — o básico que qualquer backend deveria ter desde o dia 1.
1. Validação de Entrada: Confie desconfiando
A validação de entrada é a primeira linha de defesa da sua aplicação. Tudo que vem de fora — query params, body, headers — deve ser tratado como potencialmente malicioso. Não validar os dados de entrada é o equivalente a deixar qualquer um entrar no seu sistema sem mostrar identidade.
O que pode acontecer se você ignorar isso?
- Injeção de código (SQL, NoSQL, XSS)
- Quebra de lógica da aplicação
- Travamentos por dados malformados
Para mitigar esse risco, utilize bibliotecas como zod ou joi para validar todas as entradas, exemplo com o zod.
import { z } from 'zod'
const schema = z.object({
email: z.string().email(),
password: z.string().min(6)
})
app.post('/login', (req, res) => {
const validationResult = schema.safeParse(req.body)
if (!validationResult.success) {
return res.status(400).json({
error: 'Dados inválidos.'
})
}
})
Valide primeiro, processe depois. Sempre, simples assim!
2. Não exponha erros internos
Mostrar detalhes de erro (stack trace, nomes de arquivos, variáveis internas) é como dar o mapa do castelo para quem está tentando invadi-lo. Em produção, nunca exponha detalhes técnicos.
O que pode acontecer se você mostrar erros internos?
- Exposição de estrutura interna
- Descoberta de tecnologias usadas
- Ataques dirigidos com base nas informações reveladas
app.use((err, req, res, next) => {
req.logger.error(err) // Log interno
res.status(500)
.json({
error: 'Internal server error'
})
})
3. Rate Limiting: coloque limites
Sem limitar o número de requisições por IP, sua API está aberta para ataques de força bruta, scraping excessivo e negação de serviço (DoS).
O que pode acontecer sem rate limiting?
- Derrubada do servidor por excesso de requisições
- Tentativas automatizadas de login
- Exploração de vulnerabilidades em massa
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 60 * 1000 // 1 minuto
max: 20 // até 20 requests por minuto por ip
})
app.use('/login', limiter)
Priorize rotas sensíveis: login, cadastro, recuperação de senha.
4. CORS: controle quem pode acessar sua API
CORS (Cross-Origin Resource Sharing) define quais origens podem interagir com sua API. Liberar tudo (*) é como aceitar chamadas de qualquer site, inclusive páginas falsas criadas por atacantes.
O que acontece sem controle de CORS?
- Aplicações maliciosas podem interagir com sua API em nome do usuário
- Exposição de endpoints sensíveis
- Risco de ataques CSRF combinados com sessões mal gerenciadas
import cors from 'cors'
const whiteList = ['codeinit.dev']
app.use(cors({
origin: (origin, callback) => {
if (whiteList.includes(origin)) {
return callback(null, true)
}
callback(new Error('Invalid origin'))
},
credentials: true
}))
Lembre-se: CORS mal configurado é porta aberta.
5. Proteja seus tokens JWT
Autenticação com JWT é prática, mas também é frágil se mal usada. Um token sem criptografia adequada, armazenado de forma insegura ou com validação frouxa vira um passaporte para o sistema inteiro.
O que pode acontecer se você não proteger o JWT?
- Sequestro de sessão
- Acesso indevido a dados sensíveis
- Uso de tokens expirados ou inválidos
- Veja abaixo como verificar a integridade do JWT usando uma secret
import jwt from 'jsonwebtoken'
const verifyToken = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]
if (!token) {
return res.status(401)
.json({
error: 'Unauthorized'
})
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET)
req.user = decoded
next()
} catch (err) {
req.logger.error(err) // log interno
return res.status(401)
.json({
error: 'Unauthorized'
})
}
}
6. Helmet: cabeçalhos de segurança sem dor
O Helmet adiciona automaticamente cabeçalhos HTTP que protegem sua aplicação contra ataques comuns como XSS, clickjacking e sniffing de conteúdo. Ele reduz a superfície de ataque de forma passiva e eficaz.
O que acontece sem Helmet?
- O navegador não tem diretrizes de proteção
- Seus headers expõem sua stack desnecessariamente
- Fica mais fácil para scripts maliciosos explorarem falhas conhecidas
import helmet from 'helmet'
app.use(helmet())
7. HTTPS: sem ele, tudo pode ser interceptado
Se sua aplicação ainda trafega dados em HTTP puro, você está entregando tudo de bandeja para qualquer atacante entre o cliente e o servidor.
O que pode acontecer?
- Interceptação de senhas, tokens e dados pessoais
- Ataques do tipo man-in-the-middle
- Comprometimento total da sessão do usuário
Ative HTTPS com certificados gratuitos (Let's Encrypt) ou use Cloudflare como proxy seguro.
8. Dependências: cada pacote é um risco
A maioria dos sistemas atuais depende de dezenas (ou centenas) de pacotes de terceiros. E cada um deles pode carregar vulnerabilidades.
O que pode acontecer?
- Um simples left-pad derruba sua stack
- Código malicioso se infiltra em build pipelines
- Exploração de CVEs não corrigidos
- Utilize o comando audit para verificar vulnerabilidades nos pacotes instalados
9. Logs e Monitoramento: o que você não vê, pode te matar
Sem visibilidade, você só descobre que foi atacado quando alguém te avisa — ou quando seu banco de dados já está em outro país.
O que monitorar:
- Falhas de autenticação
- Padrões anômalos de acesso
- Erros 500 frequentes
BONUS: Não confie nas suas variáveis de ambiente .env não é cofre. Variáveis de ambiente são uma forma prática de configurar sua aplicação, mas não foram feitas para garantir segurança. Tratar .env como lugar seguro é uma armadilha comum — especialmente quando você começa a colocar credenciais de banco, chaves JWT, segredos de API e tokens de terceiros ali.
O que pode dar errado?
- Seu arquivo .env pode vazar num commit sem querer
- Pode ser lido em um container ou ambiente mal configurado
- Pode ser sobrescrito em produção sem que você perceba
Boas práticas:
- Nunca envie .env para o repositório (use .gitignore com atenção)
- Use .env.example apenas com variáveis genéricas
- Em produção, configure as variáveis direto no ambiente, não em arquivos
- Rotacione segredos periodicamente — como você faria com senhas
Dica: Use ferramentas como Doppler, Vault ou mesmo o Secrets Manager da AWS para armazenar variáveis críticas fora do código-fonte. E logue alertas sempre que uma variável essencial estiver ausente ou mal configurada.
Conclusão
A segurança da sua API não é opcional, nem algo pra "depois que escalar". É o pré-requisito pra não virar estatística. Cada item citado aqui cobre falhas que já derrubaram sistemas, causaram prejuízos financeiros e deixaram devs sem dormir.
Você não precisa ser paranoico, mas precisa ser disciplinado. Faça o básico — porque a maioria não faz. A diferença entre uma API segura e um desastre é a escolha de aplicar isso agora.