Guide
Tokens JWT: Como decodificar, verificar e evitar os erros comuns
Três segmentos Base64url, uma assinatura que você deve realmente verificar e um bug histórico — alg: none — que ainda existe em algumas bibliotecas.
By Buğra SözeriPublished
Um JWT é três strings codificadas em Base64url unidas por pontos. A primeira é metadados, a segunda são os dados com que você realmente se importa, e a terceira é a assinatura que prova que os dois primeiros não foram alterados. A maioria dos bugs de JWT vem de tratar a terceira parte como opcional — ou de interpretar mal o que o formato garante em primeiro lugar.
A estrutura de três segmentos
Um JWT tem essa aparência:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNzQ4NjQ4MDAwfQ.qZdfL_HxR_eRT3z3qZX7Rqv0kK7r0sQYMfRBlLcM2hIDivida nos pontos e você obtém cabeçalho, payload, assinatura. Cada um dos dois primeiros é um objeto JSON que foi codificado em Base64url — uma variante do Base64 segura para URL que substitui + por -, / por _ e omite preenchimento. Cole qualquer token em nosso decodificador JWT para ver as três partes expandidas em JSON simples.
O cabeçalho
{
"alg": "HS256",
"typ": "JWT"
}Dois campos. alg nomeia o algoritmo de assinatura; cobrimos os valores abaixo. typ identifica o token como um JWT — algumas ferramentas o exigem, a maioria o ignora. Alguns tokens também incluem kid (ID da chave) para que o verificador saiba qual chave pública em um conjunto de chaves usar.
O payload
{
"sub": "1234567890",
"iat": 1748648000,
"exp": 1748651600,
"iss": "https://auth.example.com",
"aud": "https://api.example.com"
}Qualquer objeto JSON que você escolher. Os campos são chamados de reivindicações. O RFC 7519 define sete nomes de reivindicação “registrados” que você deve reutilizar quando seu significado corresponder ao seu.
A assinatura
A assinatura é calculada sobre Base64url(cabeçalho) + “.” + Base64url(payload) usando o algoritmo e chave indicados pelo cabeçalho. A assinatura em si é o terceiro segmento codificado em Base64url. É o que torna um JWT um JWT em vez de um simples envelope Base64.
As reivindicações registradas
- iss (emissor). Quem cunhou o token. Geralmente uma URL identificando seu serviço de autenticação. Os verificadores devem verificar se isso corresponde ao emissor esperado.
- sub (assunto).Sobre quem é o token — tipicamente o ID do usuário. Deve ser único dentro do emissor.
- aud (audiência).Para quem é o token — a URL da API ou nome do serviço. Um verificador que não corresponde à audiência esperada deve rejeitar o token, caso contrário um token destinado a um serviço pode ser reproduzido em outro.
- exp (expiração). Timestamp Unix (segundos desde 1970-01-01 UTC) após o qual o token é inválido. Os verificadores devem rejeitar tokens expirados.
- nbf (não antes). Timestamp Unix antes do qual o token é inválido. Opcional.
- iat (emitido em). Timestamp Unix de quando o token foi emitido. Informacional.
- jti (ID JWT). Um ID único para o próprio token, útil para revogação explícita ou para prevenir repetição.
HS256 vs RS256 vs ES256
HS256 (HMAC-SHA-256, simétrico)
A assinatura é um HMAC sobre o cabeçalho e o payload codificados usando um segredo compartilhado. Qualquer pessoa que possa verificar a assinatura também pode criaruma nova — a mesma chave faz os dois trabalhos.
Use HS256 quando o emissor e o verificador são a mesma parte (mesmo serviço, mesmo segredo). Não use HS256 quando terceiros precisam verificar seus tokens — você teria que compartilhar o segredo de assinatura, o que significa que eles poderiam cunhar tokens que parecem ser seus.
RS256 (RSA-SHA-256, assimétrico)
A assinatura é uma assinatura RSA usando a chave privada do emissor. Os verificadores usam a chave pública do emissor — publicada em um endpoint JWKS, tipicamente — para verificar a assinatura. Os verificadores não podem cunhar tokens.
Este é o padrão para identidade federada: OpenID Connect, Auth0, Cognito, todo fluxo de “entrar com X”. O emissor guarda a chave privada; todos os outros confiam na chave pública.
ES256 (ECDSA sobre P-256, assimétrico)
Mesmo modelo de segurança que RS256, mas com assinaturas de curva elíptica. As vantagens: tamanho de assinatura ~10× menor (64 bytes vs 256), verificação mais rápida e segurança mais forte por bit. A desvantagem: implementações ECDSA são mais fáceis de errar do que RSA, e historicamente produziram bugs críticos.
Use ES256 com uma biblioteca testada (o crypto embutido do Node, crypto/ecdsa do Go, ring do Rust, cryptography do Python). Não implemente seu próprio ECDSA. Jamais.
EdDSA (Ed25519)
Mais recente que ES256, mais difícil de usar incorretamente, o mais rápido das opções assimétricas. Listado no RFC 8037 como algoritmo JWT, mas não universalmente suportado — verifique seu stack antes de adotar.
O ataque alg: none
A especificação JWT define um valor de algoritmo especial none que significa “não assinado.” Um token com alg: nonetem um segmento de assinatura vazio — os pontos ainda estão lá, mas a terceira parte é a string vazia.
Em 2015, pesquisadores mostraram que várias bibliotecas JWT honravam o campo alg no cabeçalho como instruções para verificação. Um invasor poderia pegar um token HS256 legítimo, alterar o cabeçalho para alg: none, descartar a assinatura e a biblioteca aceitaria o token modificado como válido.
A mitigação é verificar em relação a um algoritmo esperado, não o algoritmo que o token afirma:
// ERRADO — confia no cabeçalho
jwt.verify(token, key);
// CERTO — fixa o algoritmo
jwt.verify(token, key, { algorithms: ["RS256"] });Bibliotecas modernas rejeitam none por padrão ou exigem opt-in explícito. O RFC 8725 (Melhores Práticas Atuais JWT) exige o padrão de fixação de algoritmo. Audite qualquer código de verificação que não o siga.
Outros erros comuns
Confiar no cabeçalho kid sem validação
O cabeçalho kid diz ao verificador qual chave usar de um conjunto de chaves. Se você usar cegamente kid como um caminho de arquivo ou chave de banco de dados, um invasor pode fornecer um valor malicioso (../../etc/passwd ou injeção SQL). Sempre trate kid como uma chave de pesquisa opaca em um conjunto conhecido, não como um caminho ou consulta.
Confusão de algoritmo (RS256 → HS256)
Algumas bibliotecas aceitam a chave pública para verificação RS256, mas também a aceitam como o segredo HMAC para HS256. Um invasor pode alterar o cabeçalho de RS256 para HS256, assinar o token com a chave pública RSA (publicamente conhecida) como o segredo HMAC, e a biblioteca verificará. Fixe o algoritmo.
Tokens de acesso de longa duração sem revogação
JWTs são sem estado — o verificador não fala com o emissor por solicitação. Esse é todo o ganho de desempenho, e todo o problema de revogação. Você não pode invalidar um token vazado antes de seu exp sem manter uma lista de negação do lado do servidor.
O padrão: token de acesso de curta duração (5-60 minutos) mais token de atualização de longa duração (dias a semanas) armazenado no lado do servidor. Revogue o token de atualização quando o usuário sair, quando um dispositivo for relatado como perdido ou quando um administrador forçar uma re-autenticação.
Colocar segredos no payload
O payload é codificado em Base64url, não criptografado. Qualquer pessoa com o token pode ler cada reivindicação. Não coloque senhas, chaves de API, dados pessoais sujeitos a regulamentação estrita ou qualquer outra coisa que você não escreveria em um log de servidor. Se você precisar de confidencialidade, use JWE em vez disso.
Ignorar validação de audiência e emissor
Um verificador que verifica a assinatura, mas não as reivindicações aud e iss, aceitará tokens cunhados para um serviço diferente. Em um ambiente federado, isso é um bug crítico. O RFC 8725 especifica que ambos devem ser validados em relação a valores esperados.
Armazenar JWTs em localStorage
localStorage é legível por qualquer JavaScript em sua origem, o que significa que qualquer XSS se torna um roubo completo de token. Prefira cookies HTTP-only seguros para tokens armazenados no navegador, com uma estratégia CSRF por cima.
Quando JWT é a ferramenta errada
JWTs resolvem um problema específico: um verificador precisa confiar em uma reivindicação sem contatar o emissor. Esse problema existe genuinamente em identidade federada e microsserviços. Geralmente não existe em um aplicativo web próprio onde o cliente e o servidor são operados pela mesma equipe.
Para esse caso, uma sessão regular do lado do servidor com um cookie HTTP-only seguro é mais simples:
- Imediatamente revogável. Exclua a linha de sessão, o usuário é desconectado.
- Menor na rede. Um ID de sessão é um cookie de 30 bytes; um JWT pode ter 500-2000 bytes.
- Modelo de segurança mais simples. Sem bugs de confusão de algoritmo, sem
alg: none, sem confusão de sigilo de payload. - Mais fácil de escalar do que as pessoas afirmam. Uma pesquisa Redis ou banco de dados por solicitação adiciona um milissegundo no máximo. O argumento de desempenho para JWT desaparece principalmente quando medido.
JWT vale sua complexidade quando você tem vários serviços que precisam confiar na identidade do usuário sem todos falar com um armazenamento de sessão central, ou quando você está emitindo tokens para terceiros — que é exatamente o papel que os JWTs desempenham em um fluxo de OAuth 2.0 / OpenID Connect.
Um template de verificação seguro
// Node.js, biblioteca jsonwebtoken
import jwt from "jsonwebtoken";
function verifyToken(token: string): Payload {
return jwt.verify(token, publicKey, {
algorithms: ["RS256"], // fixa o algoritmo
issuer: "https://auth.example.com", // fixa o emissor
audience: "https://api.example.com", // fixa a audiência
clockTolerance: 5, // pequena margem para desvio de relógio
}) as Payload;
}Cinco linhas de opções, quatro das quais diretamente mitigam os ataques acima. Os padrões de toda biblioteca JWT em 2026 são mais seguros do que eram em 2015, mas explícito ainda é melhor do que implícito quando o modo de falha é um bypass silencioso de autenticação.
A conclusão honesta
Decodificar um JWT é trivial — Base64url, divida, analise o JSON. Nosso decodificador no navegador faz isso sem enviar o token a lugar nenhum, o que importa porque os tokens frequentemente contêm reivindicações de identificação que você não gostaria de colar em um site de terceiros.
Verificar um JWT também é direto, contanto que você fixe o algoritmo, valide iss, aud e exp, e nunca confie no cabeçalho para lhe dizer como verificar. Escolha HS256 se o emissor e o verificador são a mesma parte; escolha RS256 ou ES256 com uma biblioteca testada caso contrário. E antes de recorrer a um JWT, verifique se um cookie de sessão comum daria conta do recado.
Frequently asked questions
- Posso confiar em um JWT apenas porque ele foi decodificado?
- Não — decodificar apenas analisa o cabeçalho e o payload em Base64url. Qualquer pessoa pode decodificar um JWT; a propriedade de segurança vem inteiramente de verificar a assinatura em relação a uma chave em que você confia. Sempre chame verify(token, key), não decode(token), em caminhos de código de produção.
- Um JWT é criptografado?
- Um JWT padrão (tecnicamente um JWS) é assinado, não criptografado — o payload é legível para qualquer pessoa que tenha o token. Se você precisar de confidencialidade, use um JWE (JSON Web Encryption). Nunca coloque senhas, chaves de API ou outros segredos em um payload JWT assumindo que eles estão ocultos.
- O que é o ataque alg: none?
- Bibliotecas JWT antigas honravam o campo alg no cabeçalho como instruções — incluindo o valor especial 'none', que significa 'sem assinatura'. Um invasor poderia alterar o algoritmo para none, descartar o segmento de assinatura e apresentar um token que a biblioteca aceitava como válido. Bibliotecas modernas rejeitam 'none' por padrão ou exigem opt-in explícito; se o código de verificação diz verify(token) em vez de verify(token, algEsperado, chave), audite-o.
- Tokens de acesso devem ter vida curta ou longa?
- Curta — minutos a uma hora no máximo. JWTs não podem ser revogados sem infraestrutura (uma lista de negação explícita derrota a premissa sem estado). Uma vida curta de token de acesso combinada com um token de atualização de longa duração armazenado no lado do servidor é o padrão: janelas breves de exposição se um token vazar, revogação fácil invalidando o token de atualização.
- Quando o JWT é realmente a escolha errada?
- Quando o cliente é um aplicativo web próprio e você controla ambos os lados. Uma sessão regular do lado do servidor com um cookie HTTP-only seguro é mais simples, imediatamente revogável, menor na rede e imune à maioria dos bugs específicos de JWT. JWT ganha sua complexidade em cenários genuinamente de terceiros — identidade federada, microsserviços que precisam confiar em um token sem chamar um serviço de autenticação central para cada solicitação — não em 'precisamos de login em nosso aplicativo React'.
- Qual é a diferença entre iat e nbf?
- iat (emitido em) é quando o token foi criado — informacional, não um limite de validade. nbf (não antes) é o momento mais cedo em que o token é válido — verificadores devem rejeitar o token antes de nbf. exp (expiração) é o momento mais tardio em que o token é válido. A maioria dos tokens define iat e exp; nbf é principalmente usado ao emitir tokens que devem se tornar válidos no futuro.
Related
- Decodificador JWTCole um token, veja o cabeçalho e o payload — localmente, nada enviado a lugar nenhum
- Glossário JWTReferência curta para o formato e suas partes
- Glossário JWSJSON Web Signature — o contêiner assinado que a maioria dos JWTs usa
- Glossário JWEJSON Web Encryption — quando você realmente precisa de confidencialidade
- Glossário de reivindicações JWTiss, sub, exp, aud e o restante das reivindicações registradas
- Hashing criptográfico explicadoPeça complementar para a discussão HS256 vs RS256 vs ES256
Published May 31, 2026