Segurança de JSON Web Tokens: Vulnerabilidades Comuns e Correções
Os JWTs são o padrão de fato para autenticação de APIs, mas sua aparente simplicidade esconde armadilhas reais de segurança. JWTs mal configurados já levaram a bypasses de autenticação, escalação de privilégios e vazamentos de dados. Este guia abrange as vulnerabilidades mais críticas e como preveni-las.
Recapitulação da Estrutura JWT
Um JWT consiste em três partes codificadas em Base64URL separadas por pontos:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.signature
- Header: Algoritmo e tipo do token
- Payload: Claims (dados do usuário, expiração, etc.)
- Signature: Verificação criptográfica
Inspecione a estrutura de JWTs com nosso Codificador/Decodificador JWT.
Para uma compreensão fundamental da estrutura JWT, veja nosso guia JWT Tokens Explicado.
Vulnerabilidades Críticas
1. Ataque de Confusão de Algoritmo
A vulnerabilidade JWT mais perigosa. Se um servidor aceita o header alg do token sem validação, um invasor pode:
Ataque: Alterar o algoritmo de RS256 (assimétrico) para HS256 (simétrico) e assinar o token forjado com a chave pública do servidor:
// Token forjado pelo invasor
header: { "alg": "HS256", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
// Assinado com a chave PÚBLICA do servidor como segredo HMAC
Se o servidor verifica tokens HS256 usando a chave pública como segredo, o token forjado passa na verificação.
Correção: Sempre especifique o algoritmo esperado explicitamente:
// ERRADO - aceita qualquer algoritmo que o token especifique
jwt.verify(token, key);
// CORRETO - forçar algoritmo específico
jwt.verify(token, key, { algorithms: ['RS256'] });
2. Ataque do Algoritmo None
Algumas bibliotecas aceitam "alg": "none" — um token sem assinatura:
// Token forjado sem assinatura
header: { "alg": "none", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
signature: "" // vazio
Correção: Nunca permita o algoritmo none em produção. Coloque explicitamente em uma lista de permitidos os algoritmos aceitos.
3. Segredos de Assinatura Fracos
JWTs baseados em HMAC (HS256/HS384/HS512) são tão fortes quanto o segredo:
// PÉSSIMO - pode ser quebrado por força bruta em segundos
secret = "password123"
// FRACO - vulnerável a ataque de dicionário
secret = "my-jwt-secret"
// FORTE - 256+ bits de aleatoriedade
secret = "a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"
Correção: Use pelo menos 256 bits de aleatoriedade criptográfica para segredos HMAC. Melhor ainda, use chaves assimétricas (RS256, ES256) onde a chave de assinatura nunca precisa ser compartilhada.
4. Expiração Ausente
Tokens sem expiração nunca expiram — um token roubado concede acesso permanente:
// ERRADO - sem expiração
{ "sub": "user123", "role": "admin" }
// CORRETO - expiração curta
{
"sub": "user123",
"role": "admin",
"exp": 1705312800,
"iat": 1705309200,
"nbf": 1705309200
}
Melhor prática: Tokens de acesso expiram em 15-60 minutos. Use refresh tokens (armazenados com segurança) para sessões longas.
5. Dados Sensíveis no Payload
Payloads de JWT são codificados em Base64URL, não criptografados. Qualquer pessoa pode decodificá-los:
echo "eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ" | base64 -d
# {"sub":"123","role":"admin"}
Nunca inclua: Senhas, chaves de API, números de cartão de crédito, dados pessoais (CPF, registros médicos) ou qualquer segredo nos payloads JWT.
6. Armazenamento de Token (XSS vs CSRF)
| Armazenamento | Vulnerável a XSS | Vulnerável a CSRF |
|---|---|---|
| localStorage | Sim | Não |
| Cookie (sem flags) | Sim | Sim |
| Cookie HttpOnly | Não | Sim |
| Cookie HttpOnly + SameSite | Não | Não |
Recomendado: Armazene JWTs em cookies HttpOnly, Secure, SameSite=Strict. Isso impede acesso por JavaScript (defesa contra XSS) e requisições cross-site (defesa contra CSRF).
Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
Checklist de Segurança
- Especifique explicitamente os algoritmos — nunca aceite do header do token
- Rejeite o algoritmo
none— sempre exija uma assinatura - Use segredos fortes — 256+ bits para HMAC, 2048+ bits para RSA
- Defina expiração curta — 15-60 minutos para tokens de acesso
- Valide todas as claims —
exp,iss,aud,nbf - Armazene em cookies HttpOnly — não em localStorage
- Rotacione segredos periodicamente — planeje a rotação de chaves
- Nunca armazene dados sensíveis no payload
- Use chaves assimétricas para sistemas distribuídos (RS256 ou ES256)
- Implemente revogação de tokens — blacklist ou expiração curta + refresh tokens
Assinatura Assimétrica vs Simétrica
| Aspecto | HS256 (Simétrico) | RS256 (Assimétrico) |
|---|---|---|
| Chave | Segredo compartilhado | Par de chaves privada/pública |
| Quem pode assinar | Qualquer um com o segredo | Apenas o detentor da chave privada |
| Quem pode verificar | Qualquer um com o segredo | Qualquer um com a chave pública |
| Distribuição de chaves | Segredo deve ser compartilhado com segurança | Apenas a chave pública é compartilhada |
| Performance | Mais rápido | Mais lento |
| Ideal para | Serviço único | Microsserviços, sistemas distribuídos |
Recomendação: Use ES256 (ECDSA) para novas aplicações — oferece segurança assimétrica com performance próxima ao HMAC.
Estratégias de Revogação de Token
JWTs são stateless por design — não há forma nativa de revogá-los. Estratégias:
- Expiração curta: Se os tokens expiram em 15 minutos, a janela de um token roubado é limitada
- Rotação de refresh token: Emita um novo refresh token a cada uso; se um refresh token for usado duas vezes, revogue todos os tokens
- Blacklist: Armazene IDs de tokens revogados (claims jti) e verifique a cada requisição
- Versionamento de token: Inclua um número de versão nas claims; incremente a versão do usuário ao fazer logout
Perguntas Frequentes
Devo usar JWT ou cookies de sessão para autenticação?
Cookies de sessão são mais simples e mais seguros para aplicações web tradicionais — o servidor controla o ciclo de vida da sessão e a revogação é instantânea. JWTs são melhores para APIs stateless, microsserviços e aplicativos móveis onde o armazenamento de sessão no servidor é impraticável. Se escolher JWTs, implemente as medidas de segurança deste guia.
Qual é o tempo ideal de expiração de JWT?
Para tokens de acesso: 15-60 minutos. Mais curto é mais seguro, mas requer renovação mais frequente. Para refresh tokens: 1-30 dias, armazenados em cookies HttpOnly. Para tokens de uso único (verificação de email, redefinição de senha): 1-24 horas.
Recursos Relacionados
- Codificador/Decodificador JWT — Inspecione e decodifique JWTs com segurança
- JWT Tokens Explicado — Entenda a estrutura e o fluxo de JWTs
- Entropia de Senhas Explicada — Força dos segredos de assinatura JWT