O Que é JWT e Como Decodificá-lo com Segurança
JSON Web Tokens aparecem em todo lugar no desenvolvimento web moderno — cabeçalhos de autenticação, fluxos OAuth, chaves de API, gerenciamento de sessão. Se você já trabalhou com alguma API que requer um token Bearer, você já usou um JWT. Mas a maioria dos devs os trata como blobs opacos e raramente olha lá dentro. Entender a estrutura de um JWT, o que cada parte significa e como decodificar um sem cometer erros de segurança leva cerca de quinze minutos. Este guia cobre tudo isso.
A Anatomia de um JWT
Todo JWT é uma string de três segmentos codificados em Base64URL separados por pontos:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzQ3MDAwMDAwLCJpYXQiOjE3NDY5OTY0MDAsInNjb3BlIjoicmVhZDp1c2VycyJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c O formato é header.payload.signature. Cada segmento codifica informações diferentes.
O Header
O primeiro segmento é o header. Decodificado, o exemplo acima se torna:
{
"alg": "RS256",
"typ": "JWT"
} alg especifica o algoritmo usado para assinar o token — RS256 significa RSA com SHA-256. typ identifica o tipo de token. Esses dois campos são o mínimo; alguns tokens também incluem kid (key ID) para dizer ao verificador qual chave pública usar quando múltiplas chaves estão em rotação.
O Payload
O segundo segmento é o payload — os dados reais. Decodificado:
{
"sub": "user_123",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1747000000,
"iat": 1746996400,
"scope": "read:users"
} O payload contém claims: declarações sobre a entidade que o token representa. Alguns claims são padronizados (chamados claims registrados); outros são específicos da aplicação (chamados claims privados). O payload é codificado em Base64URL, não criptografado. Qualquer pessoa que obtenha a string do token pode ler cada claim nele.
A Assinatura
O terceiro segmento é a assinatura. Ela é produzida pegando o header e o payload codificados, concatenando-os com um ponto e depois assinando o resultado com a chave secreta ou privada especificada em alg:
signature = sign(
base64url(header) + "." + base64url(payload),
secretOrPrivateKey
) A assinatura permite que qualquer parte com a chave pública correspondente (ou chave secreta, para algoritmos simétricos) verifique que o token foi emitido por uma fonte confiável e que nem o header nem o payload foram modificados após a emissão. Um único caractere alterado no payload invalida completamente a assinatura.
Como Decodificar um JWT
Decodificar é diferente de verificar. Decodificar simplesmente lê os dados dentro de um token. Verificar confirma que o token é autêntico e não expirou. Você deve sempre verificar em código de produção; decodificar sozinho é útil para depuração.
Passo 1: Divida o token
Divida a string JWT no caractere .. Você obterá três segmentos. Se obtiver menos de três, o token está malformado.
Passo 2: Decodifique o header e o payload
Decodifique em Base64URL cada um dos dois primeiros segmentos. O Base64URL é como o Base64 padrão, mas usa - em vez de + e _ em vez de /, sem caracteres de padding. Uma vez decodificado, você pode parsear cada segmento como JSON.
Passo 3: Analise e inspecione
Leia os claims. Verifique exp (expiração) contra o timestamp Unix atual. Olhe para iss e aud para confirmar que o token é destinado à sua aplicação. Não confie em nenhum claim até ter verificado a assinatura.
Para inspeção rápida durante o desenvolvimento, use o JWT Decoder. Ele roda inteiramente no seu browser — seu token nunca sai do seu dispositivo. Você também pode decodificar segmentos base64 manualmente com o codificador/decodificador Base64 se quiser ver os bytes brutos. Para verificar assinaturas em scripts de teste, o gerador HMAC permite reproduzir assinaturas HS256 sem escrever código.
Passo 4: Verifique a assinatura no código
Em produção, sempre verifique a assinatura usando uma biblioteca construída para sua linguagem. Não implemente a verificação de assinatura manualmente. Opções populares incluem jose ou jsonwebtoken para Node.js, PyJWT para Python, golang-jwt/jwt para Go e nimbus-jose-jwt para Java.
Sempre passe o algoritmo explicitamente. Nunca deixe a biblioteca inferir o algoritmo do header do token.
Claims Padrão (Claims Registrados)
A RFC 7519 define um conjunto de nomes de claims registrados. Eles não são obrigatórios, mas quando presentes devem seguir a semântica padrão.
iss — Issuer (Emissor)
A entidade que criou e assinou o token. Geralmente uma URL que identifica o servidor de autorização (por exemplo, https://auth.example.com). Os verificadores devem checar que iss corresponde a um valor esperado e rejeitar tokens de emissores desconhecidos.
sub — Subject (Sujeito)
O principal que o token representa — tipicamente um ID de usuário, nome de conta de serviço ou identificador de dispositivo. O valor deve ser único dentro do contexto do emissor. Sua aplicação usa sub para identificar a qual usuário a requisição pertence.
aud — Audience (Audiência)
O(s) destinatário(s) pretendido(s) do token. Se o identificador da sua API é https://api.example.com, tokens emitidos para outros serviços devem ser rejeitados. Não validar aud permite que atacantes reutilizem um token obtido de um serviço em um serviço diferente.
exp — Expiration Time (Tempo de Expiração)
Um timestamp Unix após o qual o token não deve ser aceito. Sempre valide exp. Um token sem expiração efetivamente vive para sempre — se for roubado, o atacante tem acesso indefinido.
iat — Issued At (Emitido Em)
O timestamp Unix de quando o token foi criado. Útil para detectar tokens que são tecnicamente não expirados, mas suspeisamente antigos. Você pode usar iat para impor uma idade máxima de token independente de exp.
nbf — Not Before (Não Antes)
O timestamp Unix antes do qual o token não deve ser aceito. Menos comum que exp, mas útil ao emitir tokens com antecedência — por exemplo, uma tarefa agendada que não deve começar antes de um horário específico.
jti — JWT ID
Um identificador único para o token. Permite que os emissores previnam ataques de replay de token armazenando valores jti usados e rejeitando tokens com IDs duplicados. Necessário quando você precisa de tokens de uso único, como links de redefinição de senha.
Armadilhas de Segurança
As implementações JWT têm um histórico de vulnerabilidades sutis. Estas são as mais importantes para entender antes de publicar código que aceita JWTs.
O Ataque "alg: none"
Algumas bibliotecas JWT dos primeiros dias do padrão aceitavam um token que especificava "alg": "none" em seu header. Um atacante poderia pegar qualquer token válido, substituir o algoritmo por none, remover a assinatura, e a biblioteca o aceitaria como totalmente válido — sem assinatura necessária.
A correção: sempre especifique o algoritmo esperado explicitamente ao chamar sua função de verificação. Trate qualquer token que declare alg: none como inválido. A maioria das bibliotecas modernas abordou isso, mas vale confirmar os padrões da sua biblioteca antes de ir para produção.
Confusão de Algoritmo (Downgrade RS256 para HS256)
Algumas bibliotecas que suportavam ambos os algoritmos usariam o campo alg no header do token para decidir como verificar. Um atacante poderia alterar o algoritmo para HS256, depois assinar o token usando a chave pública do servidor como segredo HMAC. O servidor, vendo HS256, verificaria contra a chave pública e aceitaria o token forjado.
A correção é a mesma: bloqueie o algoritmo no seu código de verificação. Nunca deixe o header do token influenciar qual algoritmo é usado para verificação.
Aceitando Tokens Expirados
Não validar exp é um descuido comum — às vezes introduzido quando os devs adicionam um período de carência que cresce sem limite, ou quando o caminho de código de validação é ignorado em um ramo. Trate um token expirado da mesma forma que um token ausente: rejeite-o com um 401 e exija que o cliente se re-autentique ou use um token de atualização.
Dados Sensíveis no Payload
O payload é codificado em Base64URL, não criptografado. Qualquer pessoa que intercepte o token pode decodificá-lo. Não coloque senhas, números de cartão de crédito, CPFs ou outros dados sensíveis no payload. Se você precisa transmitir claims sensíveis com segurança, use um token JWE (JSON Web Encryption), que criptografa o payload. Os JWTs assinados com JWS (o caso comum) garantem apenas autenticidade, não confidencialidade.
Validação de Audiência Ausente
Pular a validação de aud significa que um token emitido para o Serviço A pode ser reproduzido no Serviço B — desde que ambos os serviços compartilhem a mesma chave de assinatura ou confiem no mesmo emissor. Em arquiteturas de múltiplos serviços, sempre valide que a audiência do token corresponde ao identificador do seu serviço.
Melhores Práticas para 2026
Escolha RS256 para a Maioria das Aplicações
O RS256 usa uma chave privada para assinar e uma chave pública para verificar. Apenas o servidor de autorização possui a chave privada. Qualquer serviço pode verificar tokens usando a chave pública, que pode ser publicada abertamente (frequentemente via um endpoint JWKS). Se qualquer serviço individual for comprometido, os atacantes ganham a capacidade de verificar tokens — mas não de forjar novos.
O HS256 usa um único segredo compartilhado tanto para assinar quanto para verificar. Qualquer serviço que pode verificar tokens também pode criá-los. Em uma configuração de microsserviços, compartilhar o segredo entre muitos serviços aumenta o raio de explosão de uma violação.
Mantenha os Tokens de Acesso de Curta Duração
Não há mecanismo de revogação integrado para JWTs — uma vez emitido, um token é válido até expirar (a menos que você implemente uma blocklist, que reintroduz estado no servidor). Tempos de expiração curtos (15 minutos a 1 hora) limitam a janela de danos se um token for roubado. Combine tokens de acesso com tokens de atualização de longa duração armazenados em cookies HttpOnly seguros, não em localStorage.
Use Rotação de Token de Atualização
Quando um cliente usa um token de atualização para obter um novo token de acesso, emita um novo token de atualização e invalide o antigo. Dessa forma, um token de atualização roubado é detectado na próxima vez que o cliente legítimo tentar usar o original: o servidor vê um token de atualização reutilizado e pode revogar toda a sessão. Esse padrão é descrito na RFC 6819 e é amplamente suportado por servidores de autorização modernos.
Publique um Endpoint JWKS
Um endpoint de JSON Web Key Set (JWKS) (/.well-known/jwks.json por convenção) publica suas chaves públicas de assinatura em um formato padrão. Outros serviços podem buscar o JWKS e verificar tokens sem receber chaves fora de banda. Isso também torna a rotação de chaves simples: adicione a nova chave ao JWKS, comece a assinar com ela e depois remova a chave antiga assim que todos os tokens existentes expirarem.
Rotacione Chaves Periodicamente
Mesmo que sua chave privada nunca seja comprometida, rotacionar chaves periodicamente limita a janela durante a qual uma chave roubada poderia ser usada para forjar tokens. Use kid nos seus headers JWT para referenciar a chave de assinatura, para que os verificadores possam selecionar a chave pública correta do seu JWKS sem quebrar tokens existentes durante uma rotação.
Valide Todos os Claims
Verifique iss, aud, exp e nbf em cada requisição. Não pule nenhum desses porque eles parecem redundantes na sua configuração — as condições que os fazem parecer desnecessários hoje são exatamente as condições que mudam durante um incidente.
JWT vs. Cookies de Sessão
JWTs e sessões no servidor resolvem o mesmo problema — persistir o estado de autenticação em HTTP sem estado — mas com trade-offs diferentes.
JWTs são autocontidos. O servidor não precisa consultar um banco de dados para validar uma requisição. Isso os torna atraentes para sistemas distribuídos e APIs consumidas por clientes móveis ou de terceiros. A desvantagem é que a revogação requer uma blocklist (que reintroduz estado no servidor) ou tolerar o tempo de vida restante de um token comprometido.
Cookies de sessão são validados no servidor. O servidor armazena o estado da sessão e pode revogar o acesso instantaneamente deletando a sessão. Cookies com as flags HttpOnly e Secure são protegidos de acesso JavaScript e interceptação de rede. A desvantagem é que cada requisição requer uma consulta ao banco de dados, que pode se tornar um gargalo em escala.
Para aplicações web tradicionais com páginas renderizadas no servidor e um único backend, os cookies de sessão permanecem uma escolha sólida e mais simples. Para APIs consumidas por múltiplos clientes, arquiteturas de microsserviços e aplicativos móveis, os JWTs com expiração curta e rotação de token de atualização são a opção mais prática.
Decodifique Seu Token
A forma mais rápida de inspecionar um JWT é colá-lo no JWT Decoder. Ele divide o token, decodifica o header e o payload e renderiza os claims em um formato legível — tudo sem enviar seu token para lugar nenhum. Se você precisa codificar ou decodificar strings Base64URL brutas manualmente, o codificador/decodificador Base64 lida com as variantes padrão e URL-safe. Para verificar assinaturas HMAC em scripts de teste, o gerador HMAC permite reproduzir uma assinatura HS256 e compará-la com o que seu token contém.
Para a especificação completa, veja a RFC 7519 (JWT) e os exemplos interativos em jwt.io.