Segurança de JSON Web Tokens: Vulnerabilidades Comuns e Correções
Os JWTs são o padrão de facto para autenticação de APIs, mas a sua aparente simplicidade esconde verdadeiras armadilhas de segurança. JWTs mal configurados levaram a contornos de autenticação, escalação de privilégios e violações 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
- Cabeçalho: Algoritmo e tipo de token
- Payload: Claims (dados do utilizador, expiração, etc.)
- Assinatura: Verificação criptográfica
Inspecione a estrutura JWT com o nosso Codificador/Descodificador JWT.
Para uma compreensão fundamental da estrutura JWT, consulte o nosso guia JWT Tokens Explicados.
Vulnerabilidades Críticas
1. Ataque de Confusão de Algoritmo
A vulnerabilidade JWT mais perigosa. Se um servidor aceitar o cabeçalho alg do token sem validação, um atacante 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:
// Attacker's forged token
header: { "alg": "HS256", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
// Signed with the server's PUBLIC key as the HMAC secret
Se o servidor verificar tokens HS256 utilizando a chave pública como segredo, o token forjado passa na verificação.
Correção: Especifique sempre o algoritmo esperado explicitamente:
// WRONG - accepts whatever algorithm the token specifies
jwt.verify(token, key);
// CORRECT - enforce specific algorithm
jwt.verify(token, key, { algorithms: ['RS256'] });
2. Ataque de Algoritmo None
Algumas bibliotecas aceitam "alg": "none" — um token sem assinatura:
// Forged token with no signature
header: { "alg": "none", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
signature: "" // empty
Correção: Nunca permita o algoritmo none em produção. Coloque explicitamente em lista branca os algoritmos permitidos.
3. Segredos de Assinatura Fracos
JWTs baseados em HMAC (HS256/HS384/HS512) são apenas tão fortes quanto o segredo:
// TERRIBLE - can be brute-forced in seconds
secret = "password123"
// WEAK - dictionary attack vulnerable
secret = "my-jwt-secret"
// STRONG - 256+ bits of randomness
secret = "a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"
Correção: Utilize pelo menos 256 bits de aleatoriedade criptográfica para segredos HMAC. Melhor ainda, utilize chaves assimétricas (RS256, ES256) onde a chave de assinatura nunca precisa de ser partilhada.
4. Expiração em Falta
Tokens sem expiração nunca expiram — um token roubado concede acesso permanente:
// WRONG - no expiration
{ "sub": "user123", "role": "admin" }
// CORRECT - short expiration
{
"sub": "user123",
"role": "admin",
"exp": 1705312800,
"iat": 1705309200,
"nbf": 1705309200
}
Boa prática: Tokens de acesso expiram em 15-60 minutos. Utilize refresh tokens (armazenados de forma segura) para sessões longas.
5. Dados Sensíveis no Payload
Os payloads JWT são codificados em Base64URL, não encriptados. Qualquer pessoa pode descodificá-los:
echo "eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ" | base64 -d
# {"sub":"123","role":"admin"}
Nunca inclua: Palavras-passe, chaves de API, números de cartão de crédito, dados pessoais (NIF, registos médicos) ou qualquer segredo nos payloads JWT.
6. Armazenamento de Tokens (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. Isto previne o acesso por JavaScript (defesa contra XSS) e pedidos cross-site (defesa contra CSRF).
Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
Lista de Verificação de Segurança
- Especificar algoritmos explicitamente — nunca aceitar do cabeçalho do token
- Rejeitar algoritmo
none— exigir sempre uma assinatura - Utilizar segredos fortes — 256+ bits para HMAC, 2048+ bits para RSA
- Definir expiração curta — 15-60 minutos para tokens de acesso
- Validar todos os claims —
exp,iss,aud,nbf - Armazenar em cookies HttpOnly — não em localStorage
- Rodar segredos periodicamente — planear a rotação de chaves
- Nunca armazenar dados sensíveis no payload
- Utilizar chaves assimétricas para sistemas distribuídos (RS256 ou ES256)
- Implementar revogação de tokens — lista negra ou expiração curta + refresh tokens
Assinatura Assimétrica vs Simétrica
| Aspeto | HS256 (Simétrico) | RS256 (Assimétrico) |
|---|---|---|
| Chave | Segredo partilhado | 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 | O segredo deve ser partilhado de forma segura | Apenas a chave pública é partilhada |
| Desempenho | Mais rápido | Mais lento |
| Ideal para | Serviço único | Microsserviços, sistemas distribuídos |
Recomendação: Utilize ES256 (ECDSA) para novas aplicações — oferece segurança assimétrica com desempenho próximo do HMAC.
Estratégias de Revogação de Tokens
Os JWTs são stateless por design — não existe uma forma integrada de os revogar. Estratégias:
- Expiração curta: Se os tokens expiram em 15 minutos, a janela de um token roubado é limitada
- Rotação de refresh tokens: Emitir um novo refresh token a cada utilização; se um refresh token for utilizado duas vezes, revogar todos os tokens
- Lista negra: Armazenar IDs de tokens revogados (claims jti) e verificar em cada pedido
- Versionamento de tokens: Incluir um número de versão nos claims; incrementar a versão do utilizador ao terminar sessão
FAQ
Devo utilizar JWT ou cookies de sessão para autenticação?
Os 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. Os JWTs são melhores para APIs stateless, microsserviços e aplicações 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 de expiração ideal para JWT?
Para tokens de acesso: 15-60 minutos. Mais curto é mais seguro mas requer atualização mais frequente. Para refresh tokens: 1-30 dias, armazenados em cookies HttpOnly. Para tokens de utilização única (verificação de email, reposição de palavra-passe): 1-24 horas.
Recursos Relacionados
- Codificador/Descodificador JWT — Inspecione e descodifique JWTs com segurança
- JWT Tokens Explicados — Compreender a estrutura e fluxo JWT
- Entropia de Palavras-passe Explicada — Força dos segredos de assinatura JWT