JSON Web Tokens explicados: claims, assinaturas e validação segura
Um JSON Web Token, ou JWT, é um pequeno trecho de texto que carrega alguns fatos sobre um usuário ou uma requisição — quem o emitiu, a quem ele se refere, quando expira — junto com uma assinatura que permite ao destinatário verificar que ninguém os alterou. APIs aceitam JWTs como tokens de acesso, sistemas de login os emitem como tokens de ID, e muitos aplicativos web os usam como tokens de sessão. Este guia explica o que há dentro de um JWT, como os algoritmos de assinatura diferem, o que um servidor precisa verificar antes de confiar em um token, os ataques que exploram verificações ignoradas e como lidar com a expiração e o armazenamento.
O que é um JWT
O formato é definido na RFC 7519, publicada em 2015. Um JWT é um conjunto de claims (declarações) — afirmações como “o sujeito é user-4821” — codificado em JSON e depois assinado ou criptografado. Quase todo JWT em uso é assinado, como uma JSON Web Signature (JWS, RFC 7515): qualquer pessoa pode lê-lo, mas ninguém sem a chave consegue alterá-lo sem que isso seja percebido. JWTs criptografados (JWE, RFC 7516) também ocultam o conteúdo e são bem menos comuns.
Dois padrões definem os JWTs que você tem mais chance de encontrar. O OpenID Connect, a camada de login usada pelos botões “Entrar com…”, emite um token de ID que precisa ser um JWT assinado. O perfil de JWT para tokens de acesso do OAuth 2.0 (RFC 9068, 2021) define como fica um token de acesso de API quando ele é um JWT, embora os tokens de acesso de muitos provedores sejam anteriores a ele e não o sigam à risca.
As três partes
Um JWT assinado é formado por três trechos de texto em Base64URL unidos por pontos: um cabeçalho, um payload (a carga útil) e uma assinatura. Veja um token completo, dividido nos pontos:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cabeçalho: {"alg":"HS256","typ":"JWT"} — o algoritmo de assinatura e o tipo do token.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyLTQ4MjEiLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImlhdCI6MTc5MTk2ODQwMCwiZXhwIjoxNzkxOTY5MzAwLCJqdGkiOiJhOTFjMmU3ZiJ9Payload: emissor https://auth.example.com, sujeito user-4821, público https://api.example.com, emitido em 1791968400, expira em 1791969300, ID a91c2e7f.yQ_-tR1KNYT8HL6voHLYDvp5GY6EWmR33VMZ8BbD1usAssinatura: um HMAC-SHA256 das duas primeiras partes, gerado com uma chave secreta. São dados binários, não JSON.
Base64URL é o Base64 comum com dois caracteres trocados para que o resultado seja seguro em URLs (- e _ no lugar de + e /), e, nos JWTs, o preenchimento = no final é descartado. É uma codificação, não criptografia: qualquer pessoa pode transformar as duas primeiras partes de volta em JSON.
A assinatura é calculada sobre o texto exato do cabeçalho e do payload codificados, unidos pelo ponto entre eles. Mude um único caractere de qualquer um dos dois — adie a expiração, troque o sujeito por outra pessoa — e a assinatura deixa de bater.
As claims padrão
A RFC 7519 define sete claims registradas. Todas são opcionais no padrão básico; os protocolos construídos sobre ele tornam algumas delas obrigatórias.
| Claim | Nome | Significado |
|---|---|---|
iss | Emissor | Quem criou e assinou o token, geralmente uma URL |
sub | Sujeito | A quem o token se refere, geralmente um ID de usuário |
aud | Público | A quem o token se destina; um destinatário que não esteja listado deve rejeitá-lo |
exp | Momento de expiração | A partir deste momento, o token não deve ser aceito |
nbf | Não antes de | Antes deste momento, o token não deve ser aceito |
iat | Emitido em | Quando o token foi criado, o que mostra a idade dele |
jti | ID do JWT | Um identificador único, útil contra reutilização (replay) e para revogação |
As três claims de tempo (exp, nbf e iat) são timestamps Unix: números de segundos, normalmente inteiros, desde 1º de janeiro de 1970 UTC. No exemplo, 1791968400 corresponde a 14 de outubro de 2026, às 09:00 UTC, e 1791969300, a quinze minutos depois. A RFC 7519 permite que o servidor conceda uma pequena tolerância para relógios que divergem, “normalmente não mais do que alguns minutos”.
Um token de ID do OpenID Connect precisa conter iss, sub, aud, exp e iat. Um token de acesso segundo a RFC 9068 precisa conter iss, exp, aud, sub, client_id, iat e jti, e trazer o tipo at+jwt no cabeçalho. Todo o resto de um payload — um nome, um endereço de e-mail, papéis — é definido pela aplicação.
Algoritmos de assinatura
O valor alg no cabeçalho indica o algoritmo. Eles se dividem em dois grupos: segredo compartilhado, em que a mesma chave assina e verifica, e par de chaves, em que uma chave privada assina e uma chave pública verifica.
| alg | Método | Quem pode verificar | Observações |
|---|---|---|---|
HS256 | HMAC com SHA-256 e um segredo compartilhado | Qualquer pessoa com o segredo — que, com ele, também pode criar tokens | A chave deve ter pelo menos 256 bits (RFC 7518) e nunca ser uma senha (RFC 8725). |
RS256 | Assinatura RSA com SHA-256 | Qualquer pessoa com a chave pública | Chaves RSA de pelo menos 2.048 bits. É o padrão sugerido para tokens de ID do OpenID Connect. |
ES256 | ECDSA na curva P-256 com SHA-256 | Qualquer pessoa com a chave pública | Chaves e assinaturas muito mais curtas que as do RSA. |
Ed25519 | EdDSA com o conjunto de parâmetros Ed25519 | Qualquer pessoa com a chave pública | Registrado em outubro de 2025 (RFC 9864), substituindo o identificador mais antigo e menos específico EdDSA, que muitas bibliotecas ainda usam. |
none | Sem assinatura | Ninguém: o token não tem proteção | As implementações não devem aceitá-lo por padrão (RFC 7518). |
O HS256 é adequado para um único serviço que emite e verifica os próprios tokens. Quando outros serviços precisam verificar tokens — várias APIs, ou parceiros —, um par de chaves é a melhor opção: eles recebem apenas a chave pública, que não pode ser usada para criar tokens.
Decodificar não é verificar
Qualquer pessoa pode escrever um JWT com qualquer payload; decodificá-lo, como faz o decodificador do nTools, só mostra o que ele afirma. Um token só prova alguma coisa depois que o servidor que o recebe faz estas verificações, que seguem a RFC 7519 e a RFC 8725, as melhores práticas atuais da IETF para JWTs:
- Fixe os algoritmos aceitos na configuração do servidor e rejeite todo o resto. Nunca deixe que o alg do cabeçalho do token decida como ele é verificado, e use cada chave com um único algoritmo.
- Verifique a assinatura com a chave que pertence ao emissor esperado.
- Confira se iss é o emissor em que você confia e se aud indica o seu serviço.
- Exija que exp e as demais claims de que o seu serviço depende estejam presentes, e confira exp e nbf com o horário atual, com no máximo alguns minutos de tolerância. Sem isso, um token sem exp nunca expiraria.
- Quando houver um tipo de token definido, confira o typ — por exemplo, at+jwt para tokens de acesso —, para que um tipo de token não possa se passar por outro.
- Trate os campos de cabeçalho kid, jku e x5u como entrada não confiável: procure o kid na sua própria lista de chaves e obtenha chaves apenas do conjunto de chaves configurado do emissor ou de endereços que você autorizou previamente.
Na prática, essas verificações cabem a uma biblioteca de JWT com manutenção ativa, configurada com o emissor, o público e os algoritmos esperados, e não a um código escrito à mão.
Os ataques clássicos
A maioria das vulnerabilidades de JWT não está em falhas da criptografia, e sim em verificações que o servidor deixou de fazer. A RFC 8725 descreve estas:
- alg none: o atacante muda o cabeçalho para none e apaga a assinatura. Um servidor que confia no cabeçalho aceita um token que qualquer pessoa poderia ter escrito.
- Confusão de chaves: o servidor espera RS256, mas o atacante envia um token marcado como HS256, assinado com a própria chave pública do servidor como se ela fosse um segredo HMAC. Uma biblioteca que escolhe o algoritmo pelo cabeçalho verifica então o token com essa mesma chave pública — e ele passa.
- Segredos fracos: um segredo HS256 curto, ou que seja uma senha, pode ser descoberto por tentativas offline, a partir de um único token capturado, sem nenhum contato com o servidor. Com o segredo, o atacante pode assinar qualquer token.
- Substituição: um token válido emitido para um serviço, ou para uma finalidade, é apresentado a outro — um token de ID usado como token de acesso, ou um token de uma API usado em outra.
As verificações da seção anterior fecham cada uma dessas brechas: uma lista fixa de algoritmos, com cada chave vinculada a um único algoritmo, impede as duas primeiras; um segredo aleatório de pelo menos 256 bits, a terceira; e a verificação de aud e typ, a última.
O payload é público
Uma assinatura protege contra alterações, não contra leitura. Os tokens trafegam em cabeçalhos de requisição (e às vezes, de forma imprudente, em URLs) e acabam em logs, no armazenamento do navegador e em cabeçalhos Referer, e qualquer pessoa que veja um deles pode decodificá-lo. Senhas, segredos e dados pessoais de que o destinatário não precisa não devem ir em um payload.
Quando as claims precisam viajar com o token e continuar confidenciais, o token pode ser criptografado como um JWE, que tem cinco partes em vez de três. A alternativa sugerida pela OWASP costuma ser mais simples: manter os dados no servidor e entregar ao cliente um token de referência opaco, que não significa nada por si só.
Expiração, revogação e armazenamento
Um JWT assinado é válido até exp, esteja onde estiver: o servidor não precisa consultá-lo em lugar nenhum, e é isso que torna os JWTs rápidos de verificar — e também o motivo de um token roubado continuar funcionando. Prazos de expiração curtos limitam o estrago. Para invalidar um token antes disso, o servidor precisa, afinal, guardar algum estado — a OWASP descreve uma lista de bloqueio (denylist) indexada pelas claims jti e iss; uma entrada pode ser removida assim que o exp do token tiver passado.
Aplicações que precisam de sessões mais longas combinam um token de acesso de curta duração com um refresh token (token de atualização), usado para obter novos tokens de acesso. As melhores práticas de segurança do OAuth 2.0 (RFC 9700, janeiro de 2025) exigem que os refresh tokens emitidos para clientes públicos — normalmente aplicativos de navegador e de celular, que não conseguem guardar um segredo — sejam vinculados ao remetente (sender-constrained), isto é, ligados criptograficamente àquele cliente, ou rotacionados: substituídos por um novo a cada uso, com o antigo invalidado.
No navegador, a OWASP desaconselha guardar identificadores de sessão no localStorage, porque uma única falha de cross-site scripting consegue ler tudo o que está guardado ali. Um cookie marcado como HttpOnly não pode ser lido por JavaScript de forma alguma, embora também deva ser Secure e SameSite, e um script injetado na página ainda consegue enviar requisições que o levam junto.
Perguntas frequentes
Um JWT é criptografado?
Normalmente, não. A maioria dos JWTs é assinada, e o cabeçalho e o payload deles são apenas codificados em Base64URL, então qualquer pessoa que tenha o token consegue lê-los. Só um JWE, com cinco partes, oculta o conteúdo.
Dá para ler um JWT sem a chave?
Sim, a menos que seja um JWE criptografado. Em um JWT assinado, a chave é necessária para criar ou verificar a assinatura, não para ler o cabeçalho e o payload.
Por quanto tempo um JWT deve ser válido?
Os padrões não definem um número único. A recomendação habitual é o prazo mais curto que a aplicação permitir, com refresh tokens para sessões mais longas; assim, um token perdido só tem utilidade por pouco tempo.
Devo usar JWTs para sessões de sites?
Não necessariamente. Um ID de sessão clássico em um cookie pode ser revogado na hora no servidor; um JWT evita uma consulta a cada requisição, mas não pode ser invalidado antes de expirar sem que se guarde estado extra.
É seguro colar um token no decodificador do nTools?
O token é decodificado no seu navegador e não é enviado a lugar nenhum. Mesmo assim, prefira um token expirado ou de teste: um token válido é uma credencial.
Anterior da série ← Funções de hash explicadas: SHA-256, checksums e hash de senhas
Referências
- RFC 7519 — JSON Web Token (JWT) (May 2015)
- RFC 7515 — JSON Web Signature (JWS) (May 2015)
- RFC 7516 — JSON Web Encryption (JWE) (May 2015)
- RFC 7518 — JSON Web Algorithms (JWA) (May 2015)
- RFC 8725 — JSON Web Token Best Current Practices (February 2020)
- RFC 9068 — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens (October 2021)
- RFC 9700 — Best Current Practice for OAuth 2.0 Security (January 2025)
- RFC 9864 — Fully-Specified Algorithms for JOSE and COSE (October 2025)
- OpenID Connect Core 1.0 incorporating errata set 2 (December 2023)
- OWASP — JSON Web Token Cheat Sheet
- OWASP — HTML5 Security Cheat Sheet: Local Storage
Guias relacionados
- Endereços IP explicados: IPv4, IPv6 e as faixas reservadas
- Sub-redes e CIDR explicados: prefixos, máscaras e hosts utilizáveis
- Registros DNS explicados: A, AAAA, CNAME, MX, TXT e NS
- Um site está fora do ar ou é só com você? Como verificar, passo a passo
- Códigos de status HTTP e erros da Cloudflare: o que significam 403, 404, 429, 500, 503 e 521
- Timestamps Unix e fusos horários explicados
- Contagem de datas: dias entre datas, anos bissextos e semanas ISO
- Formatos de imagem explicados: JPEG, PNG, WebP, AVIF e HEIC
- Força de senhas explicada: comprimento, entropia e frases-senha
- Funções de hash explicadas: SHA-256, checksums e hash de senhas
- UUIDs explicados: v4, v7 e como escolher a versão certa
- QR codes explicados: capacidade, correção de erros e tamanho de impressão
- Porcentagens e IVA explicados: somar, tirar e acumular
- Erros comuns em JSON e como corrigi-los
- Edição de PDFs no navegador: o que se mantém ao juntar, dividir e converter