Vulnerabilidades comuns em JWT: alg:none e outros ataques
1. Introdução ao ecossistema de ameaças JWT
1.1. Por que JWTs são alvos frequentes de ataques
JSON Web Tokens (JWT) são amplamente utilizados para autenticação e autorização em APIs modernas. Sua popularidade os torna alvos constantes de ataques. A natureza stateless dos JWTs — onde o servidor não mantém sessão — significa que um token comprometido concede acesso imediato sem verificação adicional. Além disso, a confiança depositada na assinatura digital cria uma falsa sensação de segurança se a implementação for falha.
1.2. Visão geral das principais classes de vulnerabilidade
As vulnerabilidades em JWT podem ser agrupadas em:
- Manipulação do cabeçalho: alteração do algoritmo (alg, kid, jku)
- Confusão de algoritmos: troca entre simétrico e assimétrico
- Falhas de validação: omissão da verificação de assinatura
- Ataques de replay: reutilização de tokens expirados ou roubados
1.3. O papel da confiança no cabeçalho e na assinatura
O cabeçalho JWT contém metadados críticos como o algoritmo de assinatura. Se o servidor confiar cegamente nesses metadados sem validação explícita, um atacante pode manipular o token para contornar a segurança. A assinatura, por sua vez, só é eficaz se for verificada corretamente com o algoritmo e a chave apropriados.
2. Ataque alg:none — Assinatura desativada
2.1. Como o ataque funciona: manipulando o cabeçalho alg
O ataque alg:none explora bibliotecas que aceitam o valor none no campo alg do cabeçalho, indicando que nenhuma assinatura é necessária. Um token malicioso seria:
Header: {"alg":"none","typ":"JWT"}
Payload: {"sub":"admin","iat":1700000000}
Signature: (vazio)
Token completo (Base64 URL-encoded):
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTcwMDAwMDAwMH0.
2.2. Implementação insegura: bibliotecas que aceitam none por padrão
Versões antigas de bibliotecas como jsonwebtoken (Node.js) e pyjwt (Python) aceitavam alg:none por padrão. Exemplo de código vulnerável:
// Node.js - código vulnerável
const jwt = require('jsonwebtoken');
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(token, 'minha-chave-secreta'); // Aceita 'none' sem verificar
2.3. Mitigação: validação explícita do algoritmo e rejeição de none
A mitigação exige forçar o algoritmo esperado:
// Node.js - código seguro
const jwt = require('jsonwebtoken');
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(token, 'minha-chave-secreta', { algorithms: ['HS256'] });
// Rejeita automaticamente 'alg:none' e algoritmos não listados
3. Confusão de algoritmos (Algorithm Confusion)
3.1. Troca de algoritmo: de RS256 (assimétrico) para HS256 (simétrico)
Neste ataque, o servidor espera RS256 (par de chaves pública/privada), mas o atacante altera o cabeçalho para HS256 (chave secreta simétrica). Se a chave pública estiver disponível (como em JWKS endpoints públicos), o atacante a utiliza como segredo HMAC para assinar tokens.
3.2. Exploração da chave pública como segredo HMAC
Token malicioso forjado:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"admin","role":"superuser"}
Signature: HMAC-SHA256(chave_publica_conhecida, header.payload)
O servidor, ao receber o token, usa a chave pública como segredo HMAC e valida a assinatura com sucesso.
3.3. Mitigação: fixar o algoritmo esperado e validar o tipo de chave
Solução: sempre especificar o algoritmo no momento da verificação:
// Python - código seguro com PyJWT
import jwt
public_key = open('public.pem').read()
try:
decoded = jwt.decode(
token,
public_key,
algorithms=['RS256'], # Força RS256, rejeita HS256
options={'verify_exp': True}
)
except jwt.InvalidAlgorithmError:
print("Algoritmo inválido detectado!")
4. Ataques de injeção no cabeçalho JWT
4.1. Manipulação de claims via kid (Key ID) — path traversal
O campo kid é usado para selecionar a chave de verificação. Se a implementação busca um arquivo com base no valor de kid, um atacante pode fazer path traversal:
Header: {"alg":"HS256","kid":"../../etc/passwd"}
Payload: {"sub":"admin"}
Se o servidor lê o arquivo indicado por kid como chave, pode expor dados sensíveis ou usar um conteúdo controlado pelo atacante.
4.2. Injeção de parâmetros como jku e jwk para forjar chaves
Campos como jku (JWKS URL) e jwk (JSON Web Key) permitem que o token especifique sua própria chave de verificação. Se o servidor não validar a origem desses parâmetros, o atacante pode hospedar uma chave falsa e assinar tokens arbitrários.
Header: {
"alg":"RS256",
"jku":"https://atacante.com/fake-jwks.json"
}
4.3. Mitigação: sanitização de entradas e validação de fontes confiáveis
// Node.js - validação de kid
const allowedKids = ['key-01', 'key-02', 'key-03'];
const decoded = jwt.decode(token, { complete: true });
if (!allowedKids.includes(decoded.header.kid)) {
throw new Error('kid não autorizado');
}
// Para jku: restringir a URLs conhecidas e usar HTTPS com validação de certificado
5. Falhas na validação de assinatura
5.1. Omisso de verificação em bibliotecas desatualizadas
Algumas bibliotecas oferecem métodos como decode() (sem verificação) e verify() (com verificação). Desenvolvedores desatentos usam decode() para validar tokens:
// Código vulnerável - sem verificação de assinatura
const payload = jwt.decode(token); // Apenas decodifica, não verifica
5.2. Tratamento incorreto de JWTs malformados ou Base64 inválido
JWTs malformados podem causar exceções não tratadas, levando a bypass de autenticação:
// Token com padding Base64 inválido
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9==.assinatura_invalida
5.3. Mitigação: uso de bibliotecas atualizadas e testes de integridade
// Sempre usar verify() e tratar exceções
try {
const payload = jwt.verify(token, secret, { algorithms: ['HS256'] });
} catch (err) {
if (err.name === 'JsonWebTokenError') {
console.error('Token inválido:', err.message);
return res.status(401).send('Token inválido');
}
}
6. Ataques de replay e expiração inadequada
6.1. Reuso de tokens roubados sem verificação de jti (JWT ID)
Sem um identificador único (jti), tokens roubados podem ser reutilizados indefinidamente:
Payload: {"sub":"usuario123","exp":1700100000} // Sem jti
6.2. Tokens com exp ausente ou muito longo
Tokens sem expiração (exp) ou com expiração excessivamente longa (anos) aumentam a janela de ataque:
Payload: {"sub":"admin","exp":9999999999} // Expira no ano 2286
6.3. Mitigação: implementação de nonces, blacklist e rotação de tokens
// Implementação de jti e verificação de expiração
const payload = {
sub: 'usuario123',
jti: uuidv4(), // Identificador único
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hora
iat: Math.floor(Date.now() / 1000)
};
// Blacklist de tokens revogados
const blacklistedTokens = new Set();
if (blacklistedTokens.has(token)) {
return res.status(401).send('Token revogado');
}
7. Boas práticas defensivas para desenvolvedores
7.1. Checklist de validação: algoritmo, assinatura, claims e tempo
1. Algoritmo: forçar explicitamente (ex: ['HS256', 'RS256'])
2. Assinatura: sempre verificar com chave apropriada
3. Claims obrigatórias: validar sub, exp, iat, nbf
4. Tempo: verificar exp, rejeitar tokens expirados
5. jti: exigir identificador único e manter blacklist
6. kid: validar contra lista de chaves permitidas
7. jku/jwk: desabilitar ou restringir a fontes confiáveis
7.2. Uso de bibliotecas seguras e configuração explícita
// Exemplo de configuração segura com jsonwebtoken (Node.js)
const jwt = require('jsonwebtoken');
const options = {
algorithms: ['HS256'],
issuer: 'https://meu-dominio.com',
audience: 'https://api.meu-dominio.com',
clockTolerance: 30 // Tolerância de 30 segundos para diferenças de relógio
};
const payload = jwt.verify(token, secret, options);
7.3. Monitoramento e logging de tentativas de manipulação de JWT
// Logging de tentativas suspeitas
function validateToken(token) {
try {
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
return decoded;
} catch (err) {
console.warn(`Tentativa de manipulação de JWT: ${err.message}`);
console.warn(`Token: ${token.substring(0, 50)}...`);
// Alertar equipe de segurança
securityAlert(err, token);
return null;
}
}
Referências
- JWT.io - JSON Web Token Introduction — Documentação oficial e depurador interativo de JWTs, essencial para entender a estrutura e testar tokens.
- Auth0 - JWT Handbook — Guia completo sobre segurança em JWT, incluindo ataques de algorithm confusion e melhores práticas.
- OWASP - JSON Web Token Cheat Sheet — Referência da OWASP com mitigações específicas para vulnerabilidades JWT em Java.
- PortSwigger - JWT Attacks — Laboratórios práticos de exploração de vulnerabilidades JWT, incluindo alg:none e confusão de algoritmos.
- CVE-2022-23529 - jsonwebtoken Library Vulnerability — Detalhes técnicos de vulnerabilidade real em biblioteca JWT popular, demonstrando riscos de implementação.
- JWT.io - Libraries — Lista curada de bibliotecas JWT seguras por linguagem, com recomendações de versões atualizadas.