Autenticação quebrada: problemas e soluções

1. Introdução à Autenticação Quebrada

Autenticação quebrada refere-se a falhas na verificação da identidade de um usuário, permitindo que invasores assumam contas alheias ou acessem recursos restritos. Esse problema ocupa consistentemente posições de destaque no OWASP Top 10, pois é a porta de entrada para a maioria dos ataques cibernéticos. É crucial distinguir autenticação fraca (credenciais vulneráveis) de autorização inadequada (permissões incorretas após o login). Enquanto a primeira falha ocorre antes do acesso, a segunda acontece depois. Os impactos típicos incluem acesso não autorizado a dados sensíveis, roubo de identidade e violações de dados que podem custar milhões às organizações.

2. Problemas Comuns de Implementação

Senhas fracas e políticas inadequadas

Muitos sistemas ainda aceitam senhas como "123456" ou "admin". Políticas que não exigem complexidade mínima (maiúsculas, minúsculas, números e caracteres especiais) facilitam ataques de dicionário. O reuso de senhas entre sistemas amplifica o dano quando uma base é comprometida.

Exemplo de política fraca:

// Política que aceita senhas de 4 caracteres
if (senha.length < 4) {
    return "Senha muito curta";
}
// Sem validação de complexidade

Credenciais padrão e hardcoded

Dispositivos e aplicações frequentemente mantêm senhas de fábrica (admin/admin) ou credenciais fixas no código-fonte. Um repositório público no GitHub pode expor senhas de banco de dados ou APIs.

Exemplo perigoso:

// Código com credencial hardcoded
const DB_PASSWORD = "senha123";
const API_KEY = "sk-abc123def456";

Falta de proteção contra força bruta

Sistemas sem rate limiting permitem que um invasor tente milhares de combinações por minuto. Sem bloqueio de contas após tentativas falhas, o ataque pode continuar indefinidamente.

3. Falhas no Gerenciamento de Sessão

Tokens de sessão previsíveis ou expostos

IDs de sessão gerados com funções como Math.random() ou baseados em timestamp são previsíveis. Quando expostos em URLs (ex: ?sessionid=123), podem ser capturados em logs ou referenciados por terceiros.

Exemplo vulnerável:

// Geração insegura de token de sessão
function gerarToken() {
    return Math.random().toString(36).substring(2); // Previsível
}

Sessões sem expiração adequada

Timeouts de sessão de 24 horas ou mais são comuns em aplicações web. Ausência de logout efetivo (apenas fechar a aba do navegador) mantém a sessão ativa em computadores compartilhados.

Reutilização de tokens após logout

Muitas aplicações apenas removem o cookie do lado do cliente, mas mantêm a sessão ativa no servidor. Um invasor que capture o token antes do logout pode reutilizá-lo posteriormente.

4. Vulnerabilidades em Mecanismos de Recuperação

Perguntas de segurança fracas

"Qual o nome do seu primeiro animal de estimação?" ou "Qual sua cidade natal?" são informações frequentemente disponíveis em redes sociais. Respostas como "cachorro" podem ser adivinhadas em poucas tentativas.

Tokens de reset baseados em timestamp ou sequenciais (ex: reset/1001, reset/1002) permitem que um invasor tente múltiplos IDs até encontrar um válido.

Exemplo de token previsível:

// Token baseado em timestamp
function gerarTokenReset() {
    return "reset_" + Date.now(); // Previsível por ordem cronológica
}

Aceitação de tokens expirados ou reutilizados

Sistemas que não validam o ciclo de vida do token de reset permitem que tokens antigos ou já utilizados sejam reutilizados para redefinir senhas.

5. Soluções: Boas Práticas de Implementação

Políticas de senha robustas

Implemente hashing com algoritmos lentos e seguros como bcrypt, Argon2 ou PBKDF2. Nunca armazene senhas em texto puro ou com hashing rápido como MD5 ou SHA-1.

Exemplo seguro com bcrypt:

const bcrypt = require('bcrypt');
const saltRounds = 12;

async function hashSenha(senha) {
    const hash = await bcrypt.hash(senha, saltRounds);
    return hash;
}

async function verificarSenha(senha, hash) {
    return await bcrypt.compare(senha, hash);
}

Rate limiting e bloqueio progressivo

Limite o número de tentativas por IP e por conta. Aplique delays exponenciais (1s, 2s, 4s, 8s) e bloqueie a conta após 5-10 tentativas falhas consecutivas.

Exemplo de rate limiting:

// Middleware de rate limiting (express-rate-limit)
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutos
    max: 5, // 5 tentativas
    message: "Muitas tentativas. Tente novamente em 15 minutos."
});

Autenticação multifator (MFA)

Implemente um segundo fator como TOTP (Google Authenticator) ou SMS. Mesmo que a senha seja comprometida, o invasor precisa do segundo fator.

Exemplo de geração TOTP:

const speakeasy = require('speakeasy');
const secret = speakeasy.generateSecret({ length: 20 });

// Gerar token
const token = speakeasy.totp({
    secret: secret.base32,
    encoding: 'base32'
});

// Verificar token
const verified = speakeasy.totp.verify({
    secret: secret.base32,
    encoding: 'base32',
    token: tokenInput,
    window: 1
});

6. Soluções: Gerenciamento Seguro de Sessão

Geração criptograficamente segura de tokens

Utilize crypto.randomBytes (Node.js) ou secrets.token_hex (Python) para gerar tokens imprevisíveis.

Exemplo seguro:

const crypto = require('crypto');
function gerarTokenSessao() {
    return crypto.randomBytes(32).toString('hex'); // 64 caracteres hex
}

Configuração rigorosa de cookies

Defina atributos de segurança nos cookies de sessão: HttpOnly (inacessível via JavaScript), Secure (apenas HTTPS), SameSite=Strict (proteção contra CSRF) e expiração adequada.

Exemplo de configuração:

res.cookie('sessionId', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 30 * 60 * 1000 // 30 minutos
});

Invalidar sessões no logout e no servidor

No logout, destrua a sessão no servidor (remova do banco de dados ou cache) e limpe o cookie do cliente.

Exemplo de logout seguro:

async function logout(req, res) {
    await Session.destroy({ where: { id: req.session.id } });
    res.clearCookie('sessionId');
    res.redirect('/login');
}

7. Ferramentas e Testes de Verificação

Ferramentas de análise estática

Utilize Semgrep, SonarQube ou GitHub CodeQL para detectar credenciais hardcoded, uso de hashing fraco e políticas de senha inadequadas no código-fonte.

Testes de penetração automatizados

Burp Suite Community Edition e OWASP ZAP podem realizar ataques de força bruta controlados e identificar falhas de rate limiting. O Hydra é útil para testar dicionários de senhas.

Checklist de revisão de código

  • [ ] Senhas armazenadas com bcrypt/Argon2/PBKDF2?
  • [ ] Rate limiting implementado no login?
  • [ ] Tokens de sessão gerados com crypto.randomBytes?
  • [ ] Cookies configurados com HttpOnly, Secure, SameSite?
  • [ ] Logout invalida sessão no servidor?
  • [ ] Tokens de reset expiram e são de uso único?
  • [ ] MFA disponível para contas sensíveis?

8. Conclusão e Checklist Final

Autenticação quebrada continua sendo uma das vulnerabilidades mais exploradas porque muitos desenvolvedores subestimam sua complexidade. As soluções apresentadas — desde hashing robusto até gerenciamento seguro de sessão e MFA — formam uma defesa em camadas contra ataques comuns.

Checklist rápido para evitar autenticação quebrada

  1. Armazene senhas com Argon2 ou bcrypt — nunca em texto puro ou MD5/SHA-1.
  2. Implemente rate limiting — limite tentativas por IP e bloqueie contas após falhas.
  3. Gere tokens criptograficamente seguros — use crypto.randomBytes para sessão e reset.
  4. Configure cookies com HttpOnly, Secure e SameSite — previne roubo via XSS e CSRF.
  5. Invalide sessões no logout — destrua no servidor, não apenas no cliente.

Referências