Melhores práticas de segurança do JWT | Ataque alg none / Expiração / Verificação de assinatura
JWT (JSON Web Token) é o padrão de facto para autenticação em Web APIs modernas, mas <strong>implementações com interpretação incorreta da especificação podem criar vulnerabilidades facilmente</strong>. Este artigo explica padrões de uso incorreto de JWT frequentemente encontrados na prática e as melhores práticas para evitá-los.
Recapitulação da estrutura do JWT
JWT é um formato que concatena 3 cadeias de caracteres codificadas em Base64URL com um ponto.
eyJhbGci... . eyJzdWIi... . SflKxwRJSM...
header payload signature
- <strong>header</strong>: Algoritmo de assinatura (<code>alg</code>) e tipo de token (<code>typ</code>)
- <strong>payload</strong>: Objeto JSON com reclamações (sub / iss / aud / exp / iat / ...)
- <strong>signature</strong>: assinatura HMAC / RSA / ECDSA direcionada para header e payload
O conteúdo do JWT pode ser verificado com o <a href="/ja/tools/jwt/">decodificador JWT do DevLab</a>. Base64URL é uma codificação, não <strong>criptografia</strong>, portanto, não esqueça que o conteúdo do payload pode ser lido por qualquer pessoa, mesmo sem verificação de assinatura no servidor.
Ameaça 1: ataque com alg=none
A especificação JWT contém uma designação de algoritmo 「sem assinatura」 chamada <code>"alg": "none"</code>. Se um atacante usar isso para reescrever o payload com um cabeçalho <code>{"alg":"none"}</code> e a biblioteca o aceitar como está, isso se torna <strong>uma vulnerabilidade fatal que permite se passar por qualquer usuário</strong>.
<strong>Contramedidas:</strong>
- Especificar <strong>explicitamente uma whitelist</strong> dos algoritmos permitidos durante a validação
- Passar como array, como em <code>jwt.verify(token, secret, { algorithms: ['HS256'] })</code>
- As bibliotecas por volta de 2015 eram frágeis, mas as principais bibliotecas atuais têm contramedidas. No entanto, use a versão mais recente a menos que você crie a sua própria
// ✗ 悪い例 (アルゴリズム未指定 = ライブラリが alg ヘッダを信頼)
jwt.verify(token, secret);
// ✓ 良い例 (アルゴリズムを固定)
jwt.verify(token, secret, { algorithms: ['HS256'] });
Ameaça 2: ataque de confusão de chave (key confusion)
É um ataque em que um atacante que possui a chave pública RSA <strong>altera o algoritmo de RS256 para HS256</strong> para fazer a chave pública ser tratada como uma 「chave secreta」. Se a biblioteca JWT confia em <code>alg</code> para escolher a função de verificação, um token falso assinado com HMAC usando a chave pública passará.
<strong>Contramedidas:</strong> O algoritmo deve ser sempre fixado no lado da verificação (mesma contramedida que a ameaça 1). Além disso, diferencie explicitamente o tipo de chave:
- Para HS256, passe com <code>Buffer</code>
- Para RS256, passar a chave pública no formato PEM
Ameaça 3: expiração não validada / token sem prazo
A reivindicação <code>exp</code> do JWT indica a data de expiração em segundos UNIX, mas <strong>frequentemente é ignorada durante a validação</strong>. Se um token emitido uma vez puder ser usado permanentemente, os danos não cessarão quando houver vazamento.
<strong>Contramedidas:</strong>
- Definir <code>exp</code> curto no momento da emissão (o token de acesso deve ser de 15 minutos a 1 hora como referência)
- Sempre verificar <code>exp</code> durante a validação (as principais bibliotecas fazem isso automaticamente)
- Se uma sessão de longa duração for necessária, use o padrão de token de atualização (token de acesso de curta duração + token de atualização de longa duração + lista de revogação do lado do servidor)
Ameaça 4: incluir informações sensíveis em JWT
O payload do JWT é apenas codificação Base64 e qualquer pessoa pode decodificá-lo. Continuam aparecendo implementações que <strong>colocam senhas ou números de cartão de crédito no payload</strong> sem saber disso.
<strong>Contramedidas:</strong>
- O payload deve conter apenas informações de identificação mínimas (sub / user_id / role)
- Colocar informações confidenciais no banco de dados do lado do servidor e recuperá-las a partir do JWT usando user_id
- Se absolutamente necessário enviar informações confidenciais com JWT, use <strong>JWE</strong> (JWT criptografado)
Ameaça 5: Não é possível fazer logout (logout)
Se o JWT for emitido sem estado, não poderá ser revogado. Mesmo que o usuário faça logout, como o servidor não tem informações do token, 「ele pode ser usado até a expiração」. Mesmo após a alteração de senha, o JWT emitido continua válido.
<strong>Contramedidas:</strong>
- Minimize a janela de impacto com <code>exp</code> curto (15 minutos ou menos)
- Ao fazer logout, registre o <code>jti</code> (JWT ID) na <strong>lista de revogação (denylist)</strong> do lado do servidor e verifique durante a validação
- Em eventos críticos (mudança de senha・mudança de permissões), salve <code>token_version</code> no registro do usuário e confirme a correspondência durante a validação
Ameaça 6: Segredo fraco
Se o segredo HS256 for muito curto, pode ser decifrado por força bruta. Strings como <code>"secret"</code> e <code>"password123"</code> são particularmente vulneráveis.
<strong>Contramedidas:</strong>
- HS256 deve usar um valor aleatório de pelo menos <strong>256 bits (32 bytes)</strong>
- Gerar com <code>openssl rand -base64 32</code> ou <a href="/ja/tools/password/">ferramenta de geração de senha</a>
- Nunca faça commit de segredos no Git. Gerencie com variáveis de ambiente e Secrets Manager
Resumo das melhores práticas
- Fixar o algoritmo no lado da verificação (<code>algorithms: ['HS256']</code>)
- O segredo HS256 deve ter 256 bits ou mais, e para RS256, RSA com 2048 bits ou mais
- O <code>exp</code> do token de acesso é de 15 minutos a 1 hora
- Implementar sessões de longa duração com o padrão de token de atualização
- Não incluir informações confidenciais no payload (Base64 não é criptografia)
- Registre o <code>jti</code> na lista de revogação ao fazer logout
- Segredos devem ser gerenciados com variáveis de ambiente e Secrets Manager, nunca fazer commit
- Armazene JWT em HttpOnly Cookie em vez de localStorage no lado do cliente (proteção contra XSS)
Ferramentas úteis para depuração
- <a href="/ja/tools/jwt/">Decodificador JWT</a>: Visualiza header / payload / signature, exibe significado de cada claim com explicação
- <a href="/ja/tools/jwt-sign/">Ferramenta de Geração e Assinatura JWT</a>: Assine dentro do navegador usando Web Crypto API com HS256 / HS384 / HS512. Útil para gerar tokens de teste
- <a href="/ja/tools/password/">Gerador de Senha</a>: Pode ser usado para gerar segredos
Resumo
Se usado corretamente, o JWT é um token de autenticação conveniente, mas se você não conhecer as armadilhas da especificação e os padrões de ataque, introduzirá vulnerabilidades no sistema em produção. É fundamental dominar as 6 ameaças e contramedidas apresentadas neste artigo e usar a versão mais recente da biblioteca JWT. Recomendamos revisar a implementação regularmente e acompanhar os avisos de segurança.