Um JWT (JSON Web Token) é composto por três seções codificadas em Base64url unidas por pontos. Decodificar um para ler suas claims exige uma única chamada de função e nenhuma chave secreta —— justamente por isso o payload de um JWT não é um lugar seguro para dados sensíveis. Este guia explica o que é um JWT, como decodificar cada parte e a diferença crítica entre decodificar e verificar um token.
O que é um JWT?
Um JWT é um token compacto e URL-safe usado para carregar claims —— normalmente identidade e permissões de um usuário —— entre partes. É padronizado como RFC 7519. Um token se parece com isto:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSJ9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkOs dois pontos o dividem em três partes: header, payload e signature.
As três partes
- Header —— um pequeno objeto JSON que descreve o algoritmo de assinatura, ex.:
{"alg":"HS256","typ":"JWT"}. - Payload —— um objeto JSON com claims como
sub(subject),exp(expiração) e quaisquer campos personalizados. - Signature —— uma assinatura criptográfica sobre header e payload, usada para detectar adulteração. Não é legível para humanos e não é JSON.
Header e payload são JSON codificado em Base64url. A assinatura é produzida com JWS (RFC 7515).
Como decodificar um JWT em JavaScript
Divida pelos pontos e decodifique Base64url as duas primeiras partes. Note que o JWT usa Base64url (com - e _ em vez de + e /, sem padding), então converta antes de decodificar:
function decodeJwt(token) {
const [headerB64, payloadB64] = token.split('.');
const decode = (b64url) => {
const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
const json = atob(b64);
return JSON.parse(json);
};
return { header: decode(headerB64), payload: decode(payloadB64) };
} Para segurança Unicode completa, decodifique o Base64 para bytes e passe-os por TextDecoder em vez de usar só atob.
Como decodificar um JWT em Python
import base64, json
def decode_jwt(token):
header_b64, payload_b64, _sig = token.split('.')
def decode(part):
padded = part + '=' * (-len(part) % 4)
return json.loads(base64.urlsafe_b64decode(padded))
return decode(header_b64), decode(payload_b64)Decodificar não é verificar
Este é o ponto mais importante sobre JWTs. Qualquer um pode decodificar um token e ler seu payload —— sem chave. Decodificar diz o que o token afirma; não diz se essas claims são confiáveis.
Para confiar em um token, você precisa verificar a assinatura contra a chave secreta ou pública do servidor, usando uma biblioteca revisada —— nunca implemente a sua:
import jwt from 'jsonwebtoken';
// Verifica assinatura E checa expiração; lança se inválido
const claims = jwt.verify(token, process.env.JWT_SECRET);
// jwt.decode(token) —— NÃO verifica; apenas para inspeçãoA verificação no servidor é o que torna um JWT uma credencial. Tratar um payload decodificado como confiável sem verificar a assinatura é um bug clássico de autenticação.
O payload de um JWT não é criptografado
Como o payload é apenas JSON codificado em Base64url, ele oferece zero confidencialidade. É o mesmo mal-entendido tratado em Base64 não é criptografia: a codificação pode ser revertida por qualquer um.
Nunca coloque senhas, números completos de cartão ou dados pessoais sensíveis em um payload JWT. Se realmente precisar de um token criptografado, use JWE (JSON Web Encryption, RFC 7516) —— um padrão separado e bem menos comum. E evite colar tokens reais em decodificadores online não confiáveis; veja por que você não deve colar JSON sensível em ferramentas online.
Ordem de validação: assinatura primeiro, claims depois
Quando você verifica um token em produção, a ordem importa. Um checklist seguro:
- Whitelist de algoritmos —— rejeite qualquer um fora do seu conjunto esperado (ex.:
['RS256']). Nunca aceitenone. - Assinatura —— verifique com a chave certa (consulte por
kidno seu JWKS). exp—— o token não pode estar expirado.nbf(not-before) —— não deve ser usado cedo demais.iat—— sanity-check de que a emissão não está absurdamente no futuro (com tolerância de drift de relógio).iss—— o emissor deve corresponder à autoridade confiável esperada.aud—— a audience deve incluir seu serviço.
A maioria das bibliotecas JWT faz 1–4 por padrão e exige opt-in para 6–7. Até que iss e aud sejam verificados, um token válido para outro serviço do seu ecossistema poderia ser replayed contra o seu.
Claims padrão comuns
| Claim | Significado |
|---|---|
iss | Issuer —— quem criou o token |
sub | Subject —— de quem o token trata (muitas vezes um ID de usuário) |
aud | Audience —— para quem o token é destinado |
exp | Expiration —— timestamp Unix após o qual o token é inválido |
iat | Issued-at —— timestamp Unix de criação |
nbf | Not-before —— o token não vale até este momento |
O cabeçalho kid e a armadilha alg: none
Dois campos de cabeçalho aparecem com frequência em JWTs reais e vale reconhecê-los de relance:
kid(key ID) —— uma dica que diz ao verificador qual chave de um set (JWKS) assinou este token. Vive no cabeçalho para que o verificador possa escolher a chave certa antes de checar a assinatura. Tratekidcomo entrada não confiável: consulte-o numa whitelist fixa (ou num JWKS baixado), nunca o use como caminho de arquivo, parâmetro SQL ou URL.alg: "none"—— um algoritmo legal, mas historicamente perigoso, que significa „sem assinatura“. Decodificadores decodificam alegremente, e um verificador permissivo pode aceitar, transformando qualquer payload forjado em um token „válido“. Rejeitealg: noneexplicitamente e configure sua biblioteca com o(s) algoritmo(s) exato(s) esperado(s) (ex.:['RS256']) em vez de confiar no que o cabeçalho do próprio token afirma.
Decodifique um JWT no seu navegador
Para inspecionar rapidamente as claims de um token, cole qualquer uma das seções Base64url no decodificador Base64 do fixjson —— ele decodifica para o JSON subjacente localmente, sem enviar nada a um servidor. Para tokens não sensíveis você também pode usar um debugger dedicado, mas para qualquer coisa com dados reais de usuário, uma ferramenta local-first é a opção segura.
Perguntas frequentes
Como decodifico um JWT?
Divida o token pelos pontos e decodifique Base64url as duas primeiras partes (header e payload) para JSON. A terceira é a assinatura e não é legível por humanos. Nenhuma chave é necessária para decodificar.
Decodificar um JWT é o mesmo que verificá-lo?
Não. Decodificar só lê as claims; qualquer um pode fazer. Verificar checa a assinatura contra uma chave secreta ou pública para confirmar que o token é autêntico e não foi adulterado. Sempre verifique no servidor antes de confiar num token.
Qualquer um pode ler os dados de um JWT?
Sim. O payload é codificado em Base64url, não criptografado —— é texto claro para qualquer um com o token. Nunca guarde segredos em um payload JWT. Veja Base64 não é criptografia.
Qual a diferença entre JWT, JWS e JWE?
Um JWT assinado usa JWS (RFC 7515) —— payload legível, à prova de adulteração. JWE (RFC 7516) criptografa o payload para confidencialidade. Os JWTs comuns do dia a dia são JWS.
Para que serve o claim kid?
kid é um campo de cabeçalho (não um claim do payload) que identifica qual chave do JWKS assinou o token, para que o verificador escolha a certa. Consulte-o contra um conjunto fixo de chaves que você controla —— não confie nele como caminho ou URL.
Por que alg: "none" é perigoso?
Significa „sem assinatura“. Um verificador permissivo que respeita o algoritmo declarado pelo próprio token aceita qualquer payload forjado. Sempre configure sua biblioteca com o algoritmo exato esperado (ex.: ['RS256']) e rejeite none explicitamente.
Inspecione tokens localmente
- Codificador e decodificador Base64 —— decodifique as seções header/payload de um JWT no navegador
- Base64 não é criptografia —— por que qualquer um consegue ler um payload JWT
- Por que não colar JSON sensível online —— manipular tokens reais com segurança
- RFC 7519: JSON Web Token —— o padrão em si