Como decodificar e inspecionar tokens JWT

· 9 min de leitura

Os JSON Web Tokens (JWT) são a forma mais comum de gerir autenticação em aplicações web modernas. Quando algo corre mal com a autenticação, um utilizador é desligado inesperadamente, as permissões estão erradas, uma API devolve 401, descodificar o JWT é geralmente o primeiro passo de depuração. Compreender as três partes de um JWT, as claims padrão, os algoritmos com que pode ser assinado e as armadilhas comuns transforma a depuração de auth de vudu numa verificação rotineira.

Uma breve história do JWT

Os JWT foram padronizados na RFC 7519 em maio de 2015, depois de vários anos de iteração em rascunho na IETF. O formato baseou-se em desenhos anteriores de tokens compactos (assertions SAML, simples cookies opacos), mas acrescentou duas coisas que lhes faltavam: uma forma JSON estrita legível em qualquer linguagem, e uma codificação base64url-safe que sobrevivia a parâmetros de URL, cabeçalhos HTTP e campos de formulário sem novo escape. As especificações complementares, JWS (RFC 7515) para assinaturas, JWE (RFC 7516) para cifragem, e JWA (RFC 7518) para nomes de algoritmo, formam em conjunto a família JOSE (JavaScript Object Signing and Encryption).

O OAuth 2.0 e o OpenID Connect adotaram JWT como formato de token por defeito pouco depois, razão pela qual quase todos os fornecedores de auth modernos (Auth0, Okta, Cognito, Keycloak, Firebase, Supabase, Clerk) os emitem hoje. A combinação de tokens autocontidos e backends sem estado mostrou-se um encaixe muito natural para microsserviços e gateways de API. A desvantagem é que os JWT são notoriamente fáceis de usar mal, e a última década produziu um fluxo constante de CVEs em bibliotecas que não validaram o algoritmo com cuidado.

O que está dentro de um JWT

Um JWT tem três partes, separadas por pontos:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

Cabeçalho: contém o algoritmo (HS256, RS256, etc.) e o tipo de token.

{"alg": "HS256", "typ": "JWT"}

Payload: contém claims (afirmações de dados) sobre o utilizador e o token.

{"sub": "1234567890", "name": "Alice", "exp": 1700000000}

Assinatura, um hash criptográfico que verifica que o token não foi adulterado. Não consegue lê-lo sem a chave de assinatura.

Cada secção está codificada em base64url, o que significa que usa - e _ em vez de + e / e omite o padding = final. Base64url não é cifragem; colar apenas o segmento do meio em qualquer descodificador revela o payload. É por design: os segmentos do meio são desenhados para serem legíveis pelos serviços ao longo do caminho, a assinatura é a única parte que prova autenticidade.

Claims JWT comuns

As claims padrão estão registadas na IANA e definidas na RFC 7519. A maioria é opcional, mas as abaixo estão quase sempre presentes.

ClaimNome completoO que contém
subSubject (sujeito)ID ou identificador de utilizador
expExpiration (expiração)Timestamp Unix de quando o token expira
iatIssued At (emitido em)Timestamp Unix de criação do token
issIssuer (emissor)Quem criou o token (o seu servidor de auth)
audAudience (público)A quem o token se destina
nbfNot Before (não antes)O token não é válido antes deste momento
jtiJWT IDIdentificador único do token
azpAuthorized PartyA parte a quem o token foi emitido (OIDC)
scope / scpScopes OAuthPermissões concedidas, frequentemente separadas por espaço
emailE-mailIdentificador OIDC padrão de utilizador
nameNomeNome de exibição (OIDC)
nonceNonceValor OIDC de proteção contra repetição
kid (cabeçalho)Key IDQual chave de assinatura foi usada (para lookup JWKS)

Para além do conjunto padrão, as aplicações acrescentam as suas próprias claims personalizadas (roles, tenant_id, feature_flags, permissions). Os nomes de claims personalizadas não têm namespace por defeito, o que significa que dois serviços diferentes podem usar o mesmo nome para significar coisas diferentes; a convenção OIDC de prefixar com um URI (https://myapp.com/roles) evita a colisão.

Como descodificar um JWT

  1. Cole o seu token: introduza o JWT completo (formato header.payload.signature) no descodificador. Descodificadores baseados em browser processam-no localmente, o token nunca sai da página.
  2. Veja as secções descodificadas: a ferramenta mostra o cabeçalho (algoritmo), payload (claims) e assinatura como JSON formatado, com timestamps mostrados tanto como inteiros Unix como datas legíveis.
  3. Verifique as claims: examine o tempo de expiração, emissor, sujeito, público, e quaisquer claims personalizadas que conduzam a sua lógica de autorização.
  4. Compare com expectativas: cruze o emissor com o fornecedor de auth que configurou, o público com a API a que o token é enviado, e qualquer claim de role/scope com as permissões que o utilizador deveria ter.
  5. Teste de viagem no tempo: passe o rato sobre iat, nbf e exp para ver se o token é atualmente válido, vai expirar em breve, ou foi emitido há tanto tempo que a sua tolerância de desvio de relógio já não o cobre.

Algoritmos de assinatura

Nem todos os JWT usam a mesma cripto. O cabeçalho alg diz-lhe a que família pertence a assinatura, e cada uma tem propriedades de segurança muito diferentes.

AlgoritmoFamíliaTipo de chaveQuando escolher
HS256HMACSegredo partilhadoApps de um serviço; nunca partilhe o segredo entre equipas
HS384 / HS512HMACSegredo partilhadoO mesmo que HS256 com digests maiores
RS256RSAPar de chaves pública/privadaO mais comum para OIDC; verificadores só precisam da chave pública
RS384 / RS512RSAPar de chavesO mesmo que RS256 com chaves maiores
PS256 / PS384 / PS512RSA-PSSPar de chavesRSA moderno, recomendado sobre RS para novos deploys
ES256 / ES384 / ES512ECDSAPar de chaves de curva elípticaChaves menores que RSA, verificação mais rápida
EdDSAEd25519Par de chaves curva de EdwardsO mais recente, pequeno, rápido; ainda não universal
noneNenhumNenhumaProibido em produção; algumas bibliotecas antigas ainda aceitam

Algoritmos assimétricos (RS*, PS*, ES*, EdDSA) permitem que qualquer serviço verifique um token com apenas uma chave pública, daí dominarem o OIDC. Simétrico (HS*) está bem dentro de uma única aplicação mas torna-se um pesadelo para rodar ou distribuir entre vários consumidores.

Depurar com JWT

Token expirado? Verifique a claim exp. Converta o timestamp Unix para uma data legível. Se está no passado, o token expirou e precisa de ser renovado. A maioria das bibliotecas JWT rejeita tokens expirados por defeito; se a sua app os aceita, é um bug de segurança.

Permissões erradas? Procure claims de role ou scope no payload. Variam por implementação, mas parecem-se frequentemente com "role": "admin" ou "scope": "read write profile".

Problemas de identidade de utilizador? A claim sub identifica o utilizador. Verifique que corresponde ao ID de utilizador esperado. Note que alguns fornecedores usam GUIDs opacos enquanto outros usam endereços de e-mail; o descodificador mostra-lhe exatamente o que está lá.

Token não aceite? Verifique a claim aud (público). Se a API espera um valor de público específico e o token tem outro, será rejeitado. Disparidades de público são um sintoma comum de encaminhar um token para o serviço errado.

Erros 401 após deploy? Verifique a claim iss (emissor). Um novo tenant de fornecedor de auth ou uma chave de assinatura trocada altera o URL do emissor; se o seu verificador ainda confia no antigo, todos os tokens parecem inválidos.

Problemas de desvio de relógio? Se iat está ligeiramente no futuro ou exp ligeiramente no passado, o relógio do servidor pode estar a derivar. A maioria das bibliotecas JWT permite alguns segundos de margem; se não, um relógio sincronizado por NTP resolve.

Armadilhas comuns

Alternativas ao JWT

JWT é dominante mas não é a única opção. Cada alternativa troca propriedades diferentes.

MecanismoForçaFraqueza
JWT (JWS)Autocontido, fácil entre serviçosNão pode ser revogado sem estado extra
Tokens opacos + introspeçãoFácil de revogar, esconde claimsCada pedido toca no servidor de auth
Sessões do lado do servidorModelo mais simples, revogação instantâneaDifícil escalar entre serviços
PASETOSubstituto mais seguro do JWT (sem confusão alg)Ecossistema mais pequeno
MacaroonsAtenuação embutida (direitos delegados)Suporte de bibliotecas limitado
OAuth 2.0 + tokens de acesso JWTPadrão da indústria para APIsEspecificação grande, fácil de mal implementar
Tokens ID OIDCIdentidade de utilizador padrão + JWTFrequentemente confundidos com tokens de acesso
Certificados de cliente mTLSAutenticação mais forte na camada de transporteOverhead de gestão de certificados

Para a maioria das equipas, a escolha é entre JWT e tokens opacos. JWT vence quando a verificação tem de ser barata e offline; os tokens opacos vencem quando a revogação tem de ser instantânea.

Privacidade e o descodificador

O descodificador JWT corre inteiramente no seu browser. O token que cola é dividido, descodificado em base64url, e o JSON é parsado e bem-formatado sem qualquer pedido de rede. Não há registo dos tokens descodificados, nem analítica sobre as claims que contêm, nem maneira de ninguém reconstruir para quem estava a depurar. Os JWT contêm frequentemente identificadores de utilizador, endereços de e-mail, nomes de roles internos e IDs de tenant, exatamente o tipo de metadados que não quer enviar para o servidor de um estranho. Descodificar do lado do cliente mantém essa informação na sua máquina, o que é o default correto para qualquer tarefa de depuração que toque autenticação.

Perguntas frequentes

Posso verificar a assinatura de um JWT com um decodificador?

Não. A verificação de assinatura requer o segredo de assinatura ou chave pública, que fica no seu servidor. Um decodificador mostra o que há dentro do token, mas a verificação criptográfica deve acontecer no seu backend. Nunca confie em um JWT não verificado em produção.

É seguro colar um JWT em uma ferramenta online?

Sim, quando a ferramenta roda no seu navegador. Decodificadores baseados em navegador processam o token localmente, nada é enviado a um servidor. Evite ferramentas que fazem requisições de rede com seu token.

O que é a claim exp?

A claim exp (expiration) é um timestamp Unix indicando quando o token expira. Depois desse momento, o token deve ser rejeitado. Sempre verifique essa claim ao depurar problemas de autenticação.

JWTs podem ser criptografados?

JWTs padrão (JWS) são assinados mas não criptografados, qualquer um pode decodificar o payload. Tokens JWE (JSON Web Encryption) são criptografados, mas são menos comuns. Nunca coloque dados sensíveis (senhas, segredos) no payload de um JWT padrão.

What is the alg none vulnerability?

Early JWT libraries accepted tokens with an alg header set to "none", meaning the signature could be omitted entirely. An attacker who set this header could forge any payload. Modern libraries reject "none" by default, but legacy systems may still be exposed; always allow-list the expected algorithm rather than trusting the header.

How should I store a JWT on the client?

HttpOnly secure cookies with SameSite=Lax (or Strict) are the safest default; they cannot be read by JavaScript, which mitigates XSS token theft. localStorage is convenient but vulnerable to any XSS bug. Never store long-lived JWTs alongside untrusted scripts.