Para decodificar um JWT, faça o base64url-decode das duas primeiras partes separadas por ponto para ler o cabeçalho e o payload como JSON. Decodificar é apenas ler: ele ignora a assinatura, então informa o que as declarações dizem, não se são genuínas. Verificar é a etapa separada que prova que o token não foi adulterado.
Essa distinção é o jogo todo. Um JWT é assinado, não criptografado, o que significa que qualquer pessoa com o token pode ler todas as declarações contidas nele sem uma chave. Tratar "Eu decodifiquei e as declarações parecem corretas" como prova de autenticidade é como vulnerabilidades reais escapam para produção. Vamos desmontar um token, ver exatamente onde a decodificação termina e a verificação começa, e então cobrir os ataques que vivem nessa lacuna.
O que um JWT Realmente Contém#
Um JSON Web Token é composto por três seções codificadas em base64url, unidas por pontos:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTczNTY4OTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
As três partes são header.payload.signature. Decodificando as duas primeiras, você obtém JSON legível:
// Header
{ "alg": "HS256", "typ": "JWT" }
// Payload (as declarações)
{ "sub": "1234", "name": "Ada", "role": "admin", "exp": 1735689600 }
O alg informa qual algoritmo assinou o token (aqui HMAC-SHA256). O payload carrega as declarações: sub (subject/ID do usuário), role e exp (expiração como timestamp Unix). A terceira parte, a assinatura, é um hash com chave das duas primeiras. Crucialmente, o payload é apenas base64url, que é codificação, não criptografia. Nunca coloque um segredo no payload de um JWT. Qualquer um pode lê-lo.
Base64url é uma variante segura para URL do base64: troca
+e/por-e_e remove o padding. Se você já tentou decodificar um JWT com uma ferramenta base64 padrão e obteve um erro, geralmente é por causa do conjunto de caracteres.
Decodificar vs Verificar: A Distinção Que Importa#
Essas duas operações parecem semelhantes e são constantemente confundidas em tutoriais. Elas não são a mesma coisa, e misturá-las é um bug de segurança.
| Decodificar | Verificar | |
|---|---|---|
| O que faz | Lê o cabeçalho e o payload | Confirma que a assinatura é válida |
| Precisa de uma chave? | Não | Sim (chave secreta ou pública) |
| Um estranho pode fazer? | Sim, com qualquer ferramenta base64 | Não, apenas o correspondente do signatário |
| Comprova autenticidade? | Não | Sim |
| Quando usar | Depuração, inspeção de claims | Toda vez que você confiar no token |
Aqui está a diferença no código. Decodificar é trivial e não requer confiança:
// APENAS DECODIFICAR: lê claims, não prova nada
function decodeJwt(token) {
const [header, payload] = token.split(".");
const json = (part) =>
JSON.parse(atob(part.replace(/-/g, "+").replace(/_/g, "/")));
return { header: json(header), payload: json(payload) };
}
const { payload } = decodeJwt(token);
console.log(payload.role); // "admin" ... mas esse token é real?
Esse código retorna felizmente role: "admin" para qualquer token que você passar, incluindo um que um atacante forjou cinco segundos atrás. Verificar é o que torna os claims confiáveis:
// VERIFICAR: checa a assinatura e a expiração antes de confiar em qualquer coisa
import jwt from "jsonwebtoken";
try {
const claims = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ["HS256"], // fixe o algoritmo explicitamente
});
// Só agora é seguro agir com base em claims.role
grantAccess(claims.role);
} catch (err) {
// Assinatura inválida, token expirado ou algoritmo não permitido
denyAccess();
}
A regra é simples: decodifique para olhar, verifique para confiar. Qualquer caminho de código que tome uma decisão de autorização deve verificar. Decodificar sem verificar no servidor é equivalente a deixar os usuários editarem suas próprias permissões.
Os Ataques Que Vivem na Lacuna#
Quando desenvolvedores pulam ou tratam incorretamente a verificação, três ataques clássicos surgem. Todos eles exploram o fato de que o token está sob controle do atacante até que a assinatura seja verificada corretamente.
O ataque do algoritmo "none"#
A especificação JWT inclui um valor alg de none, que significa "sem assinatura." Se um servidor lê o algoritmo do próprio cabeçalho do token e confia nele, um atacante pode remover a assinatura, definir "alg": "none" e forjar qualquer payload que desejar:
// Cabeçalho forjado pelo atacante
{ "alg": "none", "typ": "JWT" }
// Payload forjado pelo atacante: admin instantâneo
{ "sub": "9999", "role": "admin" }
Com a assinatura removida e alg definido como none, um verificador ingênuo que "honra" o cabeçalho o aceita. A correção é nunca deixar o token ditar seu próprio algoritmo. Fixe o algoritmo esperado no lado do servidor, exatamente como a opção algorithms: ["HS256"] acima, para que none e qualquer outro valor inesperado sejam rejeitados de imediato.
O ataque de confusão de algoritmo (RS256 para HS256)#
Este é mais sutil e já afetou bibliotecas importantes. Suponha que seu sistema assine tokens com RS256 (assimétrico: uma chave privada assina, uma chave pública verifica). A chave pública é, por definição, pública.
Um atacante pega essa chave pública, altera o cabeçalho para "alg": "HS256" (HMAC simétrico) e assina um token forjado usando a chave pública como o segredo HMAC. Um verificador vulnerável que seleciona o algoritmo do cabeçalho agora executará a verificação HMAC usando a chave pública que já possui, e a assinatura forjada é validada. O atacante acabou de usar sua chave de verificação publicada para assinar tokens.
A defesa é o mesmo princípio: fixe o algoritmo. Se seu servidor espera RS256, configure-o para aceitar apenas RS256 e verificar com a chave pública como uma chave RSA, nunca como um segredo HMAC. Permitir que o cabeçalho escolha é a causa raiz em todos os casos.
Ignorar expiração e outras declarações#
Mesmo uma assinatura perfeitamente verificada não significa que o token é válido no momento. A verificação confirma que o token foi emitido por você e não foi alterado; ela não verifica, por si só, se o token ainda está dentro do prazo ou se é destinado a este público. Sempre valide:
exp(expiração) enbf(não antes) para que tokens expirados ou futuros sejam rejeitados.aud(público-alvo) para que um token criado para um serviço não possa ser reproduzido contra outro.iss(emissor) para que você aceite apenas tokens do provedor de identidade em que confia.
A maioria das bibliotecas verifica exp automaticamente ao chamar verify, mas aud e iss geralmente exigem que você passe os valores esperados explicitamente.
Onde você armazena e envia o token também importa#
A verificação protege o conteúdo do token, mas o manuseio do token é uma superfície de ataque própria. Alguns pontos inegociáveis:
- Prefira cookies HTTP-only em vez de
localStoragepara tokens de sessão, para que scripts do lado do cliente (e qualquer payload XSS) não possam lê-los. - Sempre use HTTPS para que o token não seja interceptado durante a transmissão.
- Mantenha os payloads mínimos e não sensíveis. Como qualquer pessoa pode decodificar o payload, nunca armazene senhas, PII completa ou segredos nele.
- Use tempos de vida curtos com tokens de atualização para que um token de acesso vazado expire rapidamente.
Um token vazado é um token válido até expirar. É por isso que as janelas de expiração e a higiene do armazenamento são tão importantes quanto a matemática de assinatura.
Como Decodificar um JWT com Segurança para Depuração#
Você precisará inspecionar tokens constantemente ao construir autenticação: verificando quais claims um provedor de identidade emitiu, depurando um erro 401, confirmando uma expiração. O problema é que um JWT é uma credencial ativa. Colar um token de sessão real em um site aleatório significa que o servidor desse site agora tem um token funcional para sua conta.
Passo 1: Use um decodificador que rode no seu navegador#
Recorra a um decodificador JWT gratuito que faz toda a análise no lado do cliente. Quando a decodificação acontece inteiramente na aba do navegador, o token nunca é transmitido para lugar nenhum, então mesmo um token de produção permanece na sua máquina. Este é o hábito mais importante ao lidar com tokens reais: confirme que a ferramenta é do lado do cliente antes de colar.
Passo 2: Leia o cabeçalho, o payload e a expiração#
O decodificador divide o token e mostra o cabeçalho e o payload como JSON formatado, além de uma expiração legível para que você não precise converter um timestamp Unix manualmente. Verifique o alg, examine os claims e confirme que exp está no futuro. Lembre-se de que você está lendo, não validando, nesta etapa.
Passo 3: Verifique a assinatura apenas com um segredo que não seja de produção#
Se o decodificador e verificador JWT permitir testar uma assinatura, cole apenas um segredo de teste ou desenvolvimento, nunca uma chave de assinatura de produção, em qualquer formulário web. A matemática da assinatura também é algo que você pode reproduzir localmente: uma assinatura HS256 é apenas um HMAC, que você pode calcular com um gerador de hash, e as partes base64url são decodificadas com um codificador e decodificador Base64. Para uma visão mais ampla sobre como lidar com tokens com segurança, veja nosso guia sobre decodificadores JWT e segurança de tokens.
A Conclusão#
Decodificar um JWT lê as claims; verificar prova que elas são autênticas. São operações diferentes com requisitos diferentes, e confundi-las é a raiz dos ataques de algoritmo none e de confusão de algoritmo. No servidor, verifique cada token, fixe o algoritmo explicitamente e cheque exp, aud e iss. Para depuração, decodifique localmente para que uma credencial ativa nunca saia do seu navegador.
Perguntas Frequentes#
Como decodificar um JWT? Divida o token nos dois pontos em três partes, depois decodifique a primeira parte (cabeçalho) e a segunda parte (payload) em JSON usando base64url. A terceira parte é a assinatura e não deve ser lida. A decodificação não requer chave e apenas mostra as claims, não confirma se o token é autêntico.
Qual é a diferença entre decodificar e verificar um JWT? Decodificar lê o cabeçalho e o payload sem precisar de chave, então qualquer um pode fazer. Verificar recalcula a assinatura com a chave secreta ou pública para confirmar que o token foi emitido por você e não foi alterado. Decodificar informa o que as claims dizem; apenas verificar diz se você deve confiar nelas.
É seguro decodificar um JWT online? Apenas com uma ferramenta que decodifica inteiramente no seu navegador. Um JWT é uma credencial ativa, então colar um token de sessão real em um site que o envia para um servidor entrega a esse servidor um token funcional para sua conta. Use um decodificador do lado do cliente para que o token nunca saia da sua aba.
O que é o ataque de algoritmo "none" em JWT?
Explora servidores que leem o algoritmo de assinatura do próprio cabeçalho do token. Um atacante define "alg": "none", remove a assinatura e forja qualquer payload. Um verificador que honra o cabeçalho aceita o token não assinado. A correção é fixar o algoritmo esperado no servidor e rejeitar none completamente.
Alguém pode ler os dados dentro do meu JWT? Sim. Um JWT é assinado, não criptografado, então o payload é apenas codificado em base64url e qualquer pessoa com o token pode decodificar e ler todas as claims. Nunca armazene senhas, segredos ou dados pessoais sensíveis no payload de um JWT. A assinatura protege contra adulteração, não contra leitura.
Como verificar se um JWT expirou?
Decodifique o payload e leia a claim exp, um timestamp Unix em segundos; se estiver no passado, o token expirou. Durante a verificação real, a maioria das bibliotecas rejeita tokens expirados automaticamente ao chamar verify. Um decodificador que mostra a expiração em formato legível permite confirmar isso rapidamente durante a depuração.



