Como Decodificar e Depurar um JWT
Um JSON Web Token é formado por três partes codificadas em Base64URL. Depois que você souber lê-las, a maioria dos erros de “token inválido” se explica em segundos. Este guia mostra como decodificar um JWT, verificar as claims que costumam dar problema e os erros que transformam JWTs em brechas de segurança.
Do que um JWT é feito
Um JWT tem o formato xxxxx.yyyyy.zzzzz. As três partes, separadas por pontos, são:
- Header: JSON com o algoritmo de assinatura (
alg) e o tipo do token, por exemplo{"alg":"HS256","typ":"JWT"}. - Payload: JSON com as claims, ou seja, para quem o token é e até quando ele vale.
- Assinatura: um hash das duas primeiras partes, feito com um segredo (HMAC) ou uma chave privada (RSA, ECDSA).
As duas primeiras partes são apenas codificadas, não criptografadas. Qualquer pessoa com o token consegue lê-las, por isso nunca coloque senhas ou dados pessoais no payload de um JWT.
Decodificar um token
Cole o token no JWT Decoder e ele mostra o header, o payload e cada timestamp convertido em data legível. A ferramenta roda no seu navegador, então os tokens de produção não são enviados para lugar nenhum. Para fazer na mão, decodifique cada parte em Base64URL: troque - por +, _ por / e adicione o padding. Ou, no Node:
const [header, payload] = token.split('.').slice(0, 2)
.map(part => JSON.parse(Buffer.from(part, 'base64url').toString()));
console.log(header, payload);
As claims que mais quebram
| Claim | Significado | Erro típico |
|---|---|---|
exp | Expiração, em segundos Unix | “jwt expired”: confira também os relógios do servidor e do cliente |
nbf | Não válido antes de | Um token usado alguns segundos antes porque o relógio do emissor está adiantado |
iat | Emitido em | Rejeitado como “emitido no futuro” depois de um desvio de relógio |
aud | Audiência para a qual o token foi feito | Um token de uma API enviado para outra: quem valida precisa checar isso |
iss | Emissor | Emissores de desenvolvimento e de produção trocados |
Os timestamps estão em segundos, não em milissegundos. Se uma expiração parece ser o ano 55.000, alguém escreveu Date.now() no lugar de Math.floor(Date.now() / 1000). O Unix Timestamp Converter resolve a dúvida. A maioria das bibliotecas aceita uma pequena tolerância de relógio (30 a 60 segundos) para absorver diferenças entre servidores.
Por que “invalid signature”?
- Segredo ou chave errados: a causa mais comum. Confira a variável de ambiente no servidor que valida, não só no que assina.
- Algoritmo errado: um token assinado com RS256 sendo validado como HS256, ou o contrário.
- O token foi editado: mudar um único caractere do payload quebra a assinatura, e é justamente essa a função.
- Rotação de chaves: as chaves do emissor mudaram. Com JWKS, associe o
kiddo header a uma chave publicada.
Para conferir um token HS256 na mão, calcule o HMAC-SHA256 de header.payload com o segredo no HMAC Generator e compare o resultado, codificado em Base64URL, com a terceira parte.
Erros de segurança a evitar
- Decodificar não é validar. Nunca confie nas claims vindas de
jwt.decode(); use a função de verificação da sua biblioteca com uma lista explícita de algoritmos permitidos. - Rejeite
alg: nonee fixe o algoritmo. Nunca deixe o token decidir como ele será verificado. - Mantenha os tokens de vida curta (minutos, para access tokens) e use refresh tokens em sessões longas. Um JWT não pode ser revogado antes de expirar sem estado extra no servidor.
- Não guarde tokens no localStorage quando um bug de XSS puder lê-los. Um cookie HttpOnly, Secure e SameSite é mais seguro para aplicações de navegador.
Precisa de um token de teste com claims específicas? Crie um com o JWT Encoder.