Segurança para programadores
Os conceitos e práticas de segurança que todo dev deveria conhecer: validação de entrada, autenticação, gestão de segredos, e as vulnerabilidades mais comuns no código do dia a dia.
Segurança não é responsabilidade só de especialistas em infosec. A maior parte das vulnerabilidades que vazam dados, derrubam sistemas ou permitem invasões vem do código que você escreve todo dia. Não é culpa sua não saber tudo sobre segurança, mas é importante estar ciente dos riscos mais comuns e saber como evitá-los.
Pense em segurança como um hábito, não como um peso. Assim como você verifica se um valor é nulo antes de usar ou se a sintaxe está correta, você também pode verificar se está validando entrada do usuário ou se está protegendo dados sensíveis. Depois que você pega o jeito, se torna natural.
Este guia foca em segurança defensiva para o dia a dia do programador: validação, autenticação, proteção de segredos, vulnerabilidades comuns e dependências. Se você quer se aprofundar em segurança ofensiva (pentest, hacking ético), existem outros caminhos. Aqui a gente cuida do que podemos fazer melhor no código que escrevemos.
As vulnerabilidades que aparecem no código do dia a dia
A OWASP Top 10 lista as vulnerabilidades mais críticas em aplicações web. Como programador, você provavelmente vai se deparar com algumas delas. Vamos aos destaques:
SQL Injection: é quando você concatena entrada do usuário diretamente em uma query SQL. O usuário consegue "fechar" a query e executar comandos próprios. Nunca faça isso:
// ERRADO - nunca faça assim
const query = "SELECT * FROM usuarios WHERE email = '" + userEmail + "'";
db.query(query);
Use prepared statements ou parameterized queries, que seu framework provavelmente oferece:
// CERTO - use placeholders
const query = "SELECT * FROM usuarios WHERE email = ?";
db.query(query, [userEmail]);
Cross-Site Scripting (XSS): quando você exibe entrada do usuário sem sanitizar, o navegador executa código malicioso. Se você deixar um usuário escrever <script>alert('hacked')</script> no perfil e outro ver sem filtro, vai executar no navegador da vítima.
Broken Authentication: usar MD5 ou SHA1 para passwords é quebrado. Usar tokens de sessão previsíveis é quebrado. Use bcrypt com cost factor >= 12. Se usar JWT, lembre que é stateless e revogação é mais complicada.
Insecure Direct Object Reference (IDOR): você consegue acessar dados de outro usuário só mudando um ID na URL. Sempre verifique se o usuário tem permissão, não só se está logado.
Sensitive Data Exposure: dados sensíveis (senhas, cartões, tokens) em logs, em cache, ou trafegando sem HTTPS.
Validação de entrada: nunca confie no que vem de fora
Você recebe dados do usuário por URL, formulário, API, arquivo. Sempre assuma que está quebrado ou é malicioso. Valide:
- Tipo: espera string? Verifica se é string mesmo.
- Tamanho: tem limite? Verifica se não passa.
- Formato: email? Data? Valida o padrão.
- Conteúdo: permite só caracteres esperados? Whitelist é melhor que blacklist.
Faça validação no servidor sempre. Validação no navegador é só UX, não segurança.
// Exemplo: validar email
function validarEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !regex.test(email)) {
throw new Error('Email inválido');
}
return email;
}
Sua framework web (Express, Django, Spring) tem bibliotecas pra isso. Use-as.
Gestão de segredos: o que não pode vazar
Chaves de API, senhas, tokens, credenciais de banco: nunca escreva literal no código. Nunca commite no git.
Use .env:
DATABASE_PASSWORD=meuSegredoAqui123
API_KEY=sk_test_xxxxx
E em .gitignore:
.env
.env.local
No código, acessa por variável de ambiente:
const dbPassword = process.env.DATABASE_PASSWORD;
const apiKey = process.env.API_KEY;
Em produção, use um secrets manager: AWS Secrets Manager, HashiCorp Vault, Supabase secrets, etc. Eles rotacionam, auditam, controlam acesso.
Use git-secrets ou trufflehog localmente pra detectar antes de commitar. Muitas equipes rodas isso no pre-commit hook.
Cuidado: .env.example pode existir no repo (sem valores reais) pra documentar que variáveis são necessárias. Mas .env nunca.
Autenticação e autorização
Muita gente confunde:
- Autenticação: quem você é? (login)
- Autorização: o que você pode fazer? (permissões)
Nunca implemente sua própria autenticação. Use bibliotecas estabelecidas: Passport.js, Django auth, Spring Security, Firebase Auth. Erros sutis são fáceis demais.
Para senhas, use bcrypt (ou Argon2). Nunca MD5, SHA1 ou SHA256 simples. Bcrypt é lento de propósito:
const bcrypt = require('bcrypt');
// Hash na criação de conta
const hash = await bcrypt.hash(senha, 12);
// Verificação no login
const match = await bcrypt.compare(senhaDigitada, hash);
Se usar JWT (tokens sem servidor), lembre que você não consegue revogar antes da expiração. Sessões com store (Redis, banco) deixam revogação mais simples.
Sempre:
- Rate limit no login (tenta muito, bloqueia por tempo)
- MFA para admin ou contas críticas
- Nunca exponha se um email existe (risco de enumeração)
Dependências: vulnerabilidades que você não escreveu
A maioria dos buracos não está no seu código. Está em npm_modules ou na venv Python. Um pacote comprometido, uma versão desatualizada com CVE conhecido.
Rode npm audit ou pip-audit regularmente. Dependabot no GitHub avisa automaticamente. Use Snyk ou OWASP Dependency-Check pra scan contínuo.
Pins de versão importam:
{
"dependencies": {
"express": "4.18.2"
}
}
Se você usar ^4.18.0, permite atualizações que podem quebrar coisas. Se usar 4.18.2, fica preso até você atualizar manualmente.
Fique ligado em typosquatting: pacotes com nomes parecidos (lodash vs lodash-es vs low-dash) que podem ser armadilhas. Verifique antes de instalar, especialmente pacotes pouco conhecidos.
Por onde começar a aplicar
Segurança não é tudo ou nada. Comece por aqui:
- Valida entrada do usuário (tipo, tamanho, formato)
- Nunca concatena entrada em SQL, usa prepared statements
- Passwords com bcrypt, cost factor >= 12
- Nunca hardcoda secrets, usa variáveis de ambiente
- Checked se tem HTTPS em produção
- Roda
npm audit/pip-auditregularmente - Verifica autorização no servidor (não só autenticação)
- CORS configurado corretamente (não
*pra tudo) - Rate limiting em endpoints críticos
Se você quer ir mais a fundo em segurança defensiva e ofensiva, veja por-onde-comecar-em-infosec.
Segurança é um processo, não um destino. Quanto mais você pensa sobre isso no dia a dia, mais natural fica.