← voltar para guias
IntermediárioDesenvolvimentoTodos

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-audit regularmente
  • 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.

← voltar para o início