O que é um JWT

Um JSON Web Token (, definido na 7519) é um jeito compacto e autocontido de transportar um conjunto de claims, ou seja, afirmações sobre uma entidade (normalmente um usuário) mais alguns metadados, num formato que quem recebe consegue verificar. É o formato de token por trás da maior parte da autenticação web moderna: o ID token do OpenID Connect é um JWT, e os access tokens do 2.0 muitas vezes também são.

"Autocontido" é a ideia central. Em vez de um ID de sessão aleatório, que o servidor precisa procurar num banco de dados, o JWT carrega as claims dentro dele mesmo, assinadas, para que o destinatário possa confiar nelas sem uma ida e volta ao servidor. Por isso os JWTs são práticos em sistemas distribuídos e stateless (sem estado), com um trade-off importante, que fica para o final.

Os três segmentos

Um JWT é formado por três segmentos codificados em Base64URL e separados por pontos:

header.payload.signature

Cada segmento tem uma função própria:

  • O header (cabeçalho) é um pequeno objeto JSON que informa o algoritmo de assinatura e o tipo do token, por exemplo {"alg":"HS256","typ":"JWT"}. O valor de alg diz ao destinatário como verificar a assinatura.
  • O payload (a carga útil) é o objeto JSON com as claims. Nele vão as claims registradas, que têm significado padronizado: iss é o emissor, sub é o sujeito, aud é o audience (para quem o token foi emitido), exp é a expiração, nbf é o not-before (antes disso, o token não vale), iat é o issued-at (quando ele foi emitido) e jti é o ID do token. Junto delas, o emissor pode incluir as claims personalizadas que quiser.
  • A assinatura (signature) é calculada sobre o header e o payload codificados, juntos, com o algoritmo indicado no header. É ela que torna confiáveis os dois primeiros segmentos.

Como o header e o payload estão só codificados em Base64URL, e não criptografados, qualquer pessoa que tenha o token consegue decodificar e ler os dois. Esta é a propriedade mais importante para fixar: o payload de um JWT é legível, não é secreto. Nunca coloque nele uma senha, um número de cartão ou qualquer dado sensível.

A assinatura: de onde vem a confiança

A assinatura é o que diferencia um JWT de qualquer string que um cliente poderia inventar. O jeito de gerá-la depende da família do algoritmo:

  • O (, HS384, HS512) assina com um único segredo compartilhado (shared secret). A assinatura é HMAC-SHA256(base64url(header) + "." + base64url(payload), secret). É simétrico: o mesmo segredo serve para criar e para verificar, então emissor e verificador precisam compartilhá-lo. É exatamente a construção que a ferramenta HMAC calcula.
  • O e o (, e outros) assinam com uma chave privada e verificam com a chave pública correspondente. Isso é assimétrico: só o emissor consegue assinar, mas qualquer um com a chave pública consegue verificar, o que serve bem para tokens conferidos por muitos serviços independentes.

Na verificação, a assinatura é recalculada ou conferida sobre o header e o payload codificados. Se um único byte de qualquer um dos dois for alterado, a assinatura deixa de bater e o token é rejeitado.

Vale conhecer duas armadilhas clássicas. O valor alg: none declara um token sem assinatura; um verificador que não o rejeita explicitamente pode ser enganado e passar a confiar em claims forjadas. E no ataque de confusão de algoritmo (algorithm confusion), o atacante pega um token RS256, troca o header para HS256 e assina o token usando a chave pública como segredo do HMAC. Isso funciona contra verificadores que pegam o algoritmo do próprio token em vez de fixá-lo. A defesa é a mesma nos dois casos: quem decide qual algoritmo é aceitável é o verificador, não o token.

Decodificar não é verificar

São duas operações diferentes, e confundir uma com a outra é uma fonte comum de bugs. Decodificar é só desfazer o Base64URL dos segmentos para ver o header e as claims: sem chave, sem confiança nenhuma, qualquer um faz. Verificar é conferir a assinatura com a chave correta e validar as claims de tempo. Só as claims de um token verificado devem servir de base para alguma ação.

O tempo também conta. exp (expiração) e nbf (not-before) são timestamps que delimitam a janela de validade do token, e iat registra quando ele foi emitido. Um verificador correto rejeita um token expirado ou que ainda não começou a valer, normalmente com uma pequena tolerância para diferença de relógio (clock skew).

O que um JWT não é

Um JWT é assinado, não criptografado (criptografia é outro padrão, o JWE). Ele prova quem emitiu as claims e que elas não foram adulteradas; ele não as esconde.

Um JWT assinado também não é fácil de revogar por si só. Como as claims ficam dentro do token e a verificação não depende de nenhuma consulta ao servidor, o token continua válido até expirar, mesmo que você queira cancelá-lo antes. Na prática, a resposta é usar tokens de validade curta e, quando a revogação precisa ser imediata, um mecanismo separado (um refresh token, uma lista de revogação ou token introspection).

A ferramenta JWT decodifica o header e as claims de um token, mostra em linguagem simples a expiração e os demais prazos, e verifica uma assinatura HS256, HS384 ou HS512 com um segredo que você cola, tudo no seu navegador. O token e o segredo nunca são enviados para lugar nenhum.

Esta série, em ordem

Este artigo abre a série JWT e . Ele desmonta um token e separa decodificar de verificar; os outros artigos trazem, na ordem, tudo de que a verificação precisa: o algoritmo, a chave e seus parâmetros, o conjunto de chaves publicado e a rotação dele, a sequência rígida que vai do kid até a assinatura, e as falhas de quem pula um passo ou confia no header do próprio token.

  1. Algoritmos de assinatura de JWT: HMAC, RSA e ECDSA. Por que o header alg importa, assinatura simétrica versus assimétrica, e como escolher entre HMAC, RSA e ECDSA.
  2. Tipos de chave JWK: RSA, EC, OKP e oct. O campo kty e os quatro tipos de chave que cobrem quase todos os casos: RSA, EC, OKP e oct.
  3. Parâmetros e thumbprints de JWK. Para que serve uma chave e como ela é identificada: kid, use e o thumbprint calculado da RFC 7638.
  4. JWKS e rotação de chaves: como os provedores publicam suas chaves. O conjunto de chaves que um provedor publica para que qualquer um possa verificar os tokens dele, e como trocar as chaves sem invalidar esses tokens.
  5. Como verificar um JWT com um JWKS: do kid à assinatura. A sequência curta e rígida: ler o header, achar a chave pelo kid, confirmar o algoritmo, conferir a assinatura.
  6. Armadilhas de segurança em JWT: alg:none, confusão de chave e verificações ausentes. alg:none, confusão de chave e checagens de claims que faltam: os poucos erros de sempre, e a validação que todo verificador precisa fazer.
  7. Ataques de confusão de algoritmo em JWT. Confiar no algoritmo que o próprio token declara: aceitar none, ou verificar um RS256 como HS256 usando a chave pública como segredo.