Pular para o conteúdo
Categoria: Segurança da Informação12 min de leitura

O que é JWT (JSON Web Token)?

Por Lucas Andrade ·

Entenda a estrutura, a assinatura e o uso correto de JWT para autenticacao stateless em APIs e apps, alem dos erros mais comuns e ataques que voce precisa evitar.

O que é JWT (JSON Web Token)?

Se você já fez login em algum app construído com IA e reparou em uma string longa e estranha viajando entre o navegador e o servidor, há grandes chances de você ter visto um JWT. Esse formato virou o padrão de fato para representar identidade e permissões de forma compacta e verificável. Neste artigo você vai entender o que é um JSON Web Token, como ele é construído, quando faz sentido usá-lo e quais armadilhas derrubam aplicações reais.

O que é um JWT, afinal

Um JWT (JSON Web Token) é um formato padronizado para transportar informações entre duas partes de maneira compacta e segura, definido na RFC 7519 (Jones, Bradley, Sakimura, 2015). Na prática, é uma string de texto que carrega um conjunto de dados em formato JSON — chamados de claims — junto com uma assinatura que permite verificar se o conteúdo não foi adulterado.

A ideia central é simples: em vez de o servidor guardar o estado da sessão em memória ou banco, ele entrega ao cliente um token que carrega a própria identidade do usuário. Quando o cliente faz uma nova requisição, ele envia esse token de volta, e o servidor apenas verifica a assinatura para confiar no conteúdo. Por isso dizemos que o JWT habilita autenticação stateless (sem estado no servidor).

Antes de seguir, vale fixar uma distinção fundamental: um JWT geralmente prova quem você é, mas o que você pode fazer é uma decisão à parte. Se essa fronteira ainda está nebulosa, vale ler Autenticação vs autorização: qual a diferença?, porque ela explica por que um token válido não é um cheque em branco.

O JWT é apenas o conteúdo. A "embalagem" criptográfica que envolve esse conteúdo é definida por padrões irmãos: o JWS (JSON Web Signature, RFC 7515) cuida de assinar, e o JWE (JSON Web Encryption) cuida de criptografar. Na imensa maioria dos casos, quando alguém fala "JWT", está se referindo a um JWS — um token assinado, mas não criptografado. Guardar essa diferença ajuda a entender por que o conteúdo é público por padrão.

A anatomia de um JWT: três partes

Um JWT é formado por três blocos separados por pontos:

xxxxx.yyyyy.zzzzz

Esses três blocos são, na ordem: header (cabeçalho), payload (carga útil) e signature (assinatura). Cada um dos dois primeiros é um objeto JSON codificado em Base64URL — uma variação do Base64 segura para uso em URLs. É importante entender que Base64 não é criptografia: qualquer pessoa pode decodificar e ler o conteúdo. O que protege o token é a assinatura, não o embaralhamento.

O header

O cabeçalho descreve o tipo do token e o algoritmo usado na assinatura:

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

Aqui alg indica o algoritmo (por exemplo, HS256 para HMAC com SHA-256) e typ confirma que é um JWT. Em sistemas que usam chaves assimétricas e rotacionam chaves, é comum aparecer também um campo kid (key ID), que diz qual chave pública deve ser usada para verificar aquele token específico.

O payload

O payload guarda os claims, que são afirmações sobre o usuário ou sobre o próprio token:

{
  "sub": "1234567890",
  "name": "Ana Souza",
  "role": "editor",
  "iat": 1716200000,
  "exp": 1716203600
}

A RFC 7519 define um conjunto de claims registrados que você deveria conhecer:

  • sub (subject): identifica o sujeito do token, normalmente o ID do usuário.
  • iss (issuer): quem emitiu o token.
  • aud (audience): para qual serviço o token se destina.
  • exp (expiration time): timestamp em que o token expira.
  • iat (issued at): quando o token foi emitido.
  • nbf (not before): a partir de quando o token é válido.

Além desses, você pode adicionar claims customizados como role ou email. Lembre-se: como o payload é apenas codificado, nunca coloque dados sensíveis ali (senhas, números de cartão, segredos).

Um detalhe que decorre disso e pega muita gente: autorização não deve viver inteiramente no token. Como o JWT é emitido em um momento e usado depois, qualquer permissão que você "congele" dentro dele pode ficar desatualizada. Se um usuário é rebaixado de admin para editor, um token antigo ainda dirá admin até expirar. Por isso, decisões de autorização sensíveis costumam ser resolvidas ao vivo no servidor, não lidas cegamente de um claim.

A assinatura

A assinatura é o que dá confiança ao token. Ela é gerada aplicando o algoritmo do header sobre o header e o payload codificados, usando uma chave secreta:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  segredo
)

Se alguém alterar um único caractere do payload — por exemplo, trocar "role": "editor" por "role": "admin" — a assinatura deixa de bater e o servidor rejeita o token. Para entender por baixo dos panos como funciona esse tipo de função, vale a leitura de O que é criptografia? Simétrica, assimétrica e hashing.

Como a assinatura realmente protege o token

Existem duas grandes famílias de algoritmos de assinatura para JWT:

  1. Simétricos (HMAC): usam uma única chave secreta compartilhada para assinar e verificar. O HS256 é o exemplo clássico. É simples, mas exige que todos que precisam verificar o token também conheçam o segredo que assina — o que pode ser arriscado quando há muitos serviços.
  1. Assimétricos (RSA/ECDSA): usam um par de chaves — uma privada para assinar e uma pública para verificar. O RS256 é o mais comum. A grande vantagem é que você distribui a chave pública livremente; quem a tem consegue verificar, mas não consegue forjar tokens. Isso é ideal em arquiteturas com vários serviços ou quando terceiros precisam validar tokens.

A escolha entre simétrico e assimétrico tem implicações de segurança importantes, e novamente o artigo sobre O que é criptografia? Simétrica, assimétrica e hashing aprofunda essa diferença.

Na prática, sistemas que usam chaves assimétricas costumam expor as chaves públicas em um endpoint padronizado chamado JWKS (JSON Web Key Set). O serviço que valida tokens busca esse conjunto de chaves, encontra a chave certa pelo kid do header e verifica a assinatura — tudo sem nunca conhecer a chave privada. Isso é o que permite rotacionar chaves sem reimplantar todos os serviços de uma vez.

O ciclo de vida: login, uso e expiração

O fluxo típico de uso de JWT em um app web ou mobile costuma ser assim:

  1. O usuário envia credenciais (e-mail e senha) para o endpoint de login.
  2. O servidor valida as credenciais e, se estiverem corretas, emite um JWT assinado com exp curto (por exemplo, 15 minutos).
  3. O cliente armazena o token e o envia em toda requisição subsequente, normalmente no header HTTP:
GET /api/perfil HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  1. O servidor verifica a assinatura e os claims (exp, aud, iss) e, se tudo estiver válido, atende a requisição.

Como o token tem vida curta por segurança, costuma-se usar um refresh token de vida mais longa, guardado com mais cuidado, para obter novos JWTs sem forçar o usuário a logar de novo. O JWT é também a peça que circula em protocolos de delegação como o descrito em O que é OAuth 2.0? Delegação de acesso explicada, onde tokens representam permissões concedidas a aplicativos terceiros.

Access token vs refresh token

Vale separar bem os papéis dos dois tokens, porque confundi-los gera furos de segurança:

  • O access token (o JWT em si) é de vida curta e enviado em toda requisição. Se vazar, o estrago é limitado pelo tempo curto de expiração.
  • O refresh token é de vida longa, fica guardado com mais cuidado (idealmente em um cookie HttpOnly) e só serve para pedir novos access tokens. Diferente do JWT stateless, o refresh token geralmente é rastreado no servidor, o que permite revogá-lo de imediato — fechando a maior fraqueza do modelo puramente stateless.

Esse desenho de "token curto sem estado + token longo com estado" é o que a maioria das aplicações sérias adota, porque combina a escalabilidade do JWT com a capacidade de revogação que ele sozinho não tem.

JWT vs sessões tradicionais

Vale comparar os dois modelos para entender quando cada um brilha:

| Aspecto | Sessão no servidor | JWT (stateless) | |---|---|---| | Onde fica o estado | Banco/memória do servidor | No próprio token, com o cliente | | Escalabilidade horizontal | Exige sessão compartilhada | Natural — qualquer instância verifica | | Revogação imediata | Fácil (apaga a sessão) | Difícil (token vale até expirar) | | Tamanho da requisição | Pequeno (só o ID) | Maior (token inteiro vai junto) |

O ponto sobre revogação é o calcanhar de Aquiles do JWT: como o servidor não guarda estado, ele não consegue, por padrão, invalidar um token antes do exp. Estratégias comuns para mitigar isso incluem listas de bloqueio (denylist) de tokens, tempos de expiração curtos e rotação de chaves de assinatura.

Um esclarecimento útil: JWT não é "melhor" que sessão de servidor — são escolhas com trade-offs diferentes. Sessões tradicionais continuam excelentes para um app monolítico onde revogação instantânea importa muito. O JWT brilha quando você tem múltiplos serviços, precisa escalar horizontalmente ou quer que terceiros validem identidade sem chamar de volta o servidor de autenticação a cada requisição.

Erros comuns que você deve evitar

Muitos incidentes de segurança com JWT vêm de implementações descuidadas. Os mais perigosos:

  • Aceitar alg: none: alguns parsers antigos aceitavam tokens sem assinatura quando o algoritmo era none. Sempre fixe explicitamente os algoritmos aceitos na verificação.
  • Confundir a chave de HMAC com a pública de RSA: existe um ataque clássico em que o atacante muda o algoritmo de RS256 para HS256 e usa a chave pública (que é conhecida) como segredo de HMAC. Bloqueie isso validando o algoritmo esperado.
  • Não validar exp, aud e iss: verificar só a assinatura não basta. Um token válido para outro serviço pode ser reaproveitado se você ignorar a audiência.
  • Guardar dados sensíveis no payload: lembre-se de que o payload é legível por qualquer um.
  • Armazenar o token de forma insegura no navegador: guardar JWT em localStorage o expõe a roubo via XSS; usar cookies traz outras preocupações, como ataques abordados em O que é CSRF e como evitar requisições forjadas.

O ataque de confusão de algoritmo, em detalhe

Por ser o mais sutil, o ataque de "algorithm confusion" merece um exemplo. Imagine um serviço que verifica tokens RS256 usando a chave pública — que, por definição, é conhecida por todos. Se o código de verificação for ingênuo e simplesmente "ler o alg do header e usar a chave configurada", um atacante pode:

  1. Pegar a chave pública (que é distribuída abertamente).
  2. Forjar um token novo, trocando o alg para HS256.
  3. Assinar esse token com HMAC usando a chave pública como se fosse o segredo.

Se o servidor usar a mesma chave para verificar tanto RSA quanto HMAC, a assinatura "bate" e o token forjado é aceito — com qualquer claim que o atacante quiser, inclusive "role": "admin". A defesa é direta: nunca deixe o token escolher o algoritmo. Configure a verificação para aceitar exatamente um algoritmo esperado e rejeite todo o resto.

// pseudocódigo da verificação segura
verificar(token, {
  algoritmosPermitidos: ["RS256"],   // lista fixa, nunca lida do header
  audiencia: "minha-api",
  emissor: "https://auth.exemplo.com"
})

JWT no contexto de APIs

Quando você constrói uma API que será consumida por front-ends, apps móveis ou outros serviços, o JWT se encaixa muito bem porque cada requisição carrega sua própria prova de identidade. Ainda assim, o token é apenas uma peça de um conjunto maior de defesas. Para ver como ele se combina com rate limiting, validação de entrada e outras camadas, vale o aprofundamento em Segurança de [APIs](/seguranca-de-apis): protegendo seus endpoints.

Um bom hábito é tratar a verificação do JWT como um middleware central: um único ponto que valida assinatura e claims antes de qualquer rota protegida ser executada. Isso evita o erro recorrente de proteger algumas rotas e esquecer outras.

Perguntas frequentes

JWT é criptografado? Por padrão, não. O JWT mais comum é assinado (JWS), o que garante integridade, mas o conteúdo é apenas codificado em Base64URL e pode ser lido por qualquer um. Para confidencialidade real do conteúdo, é preciso usar JWE.

Posso invalidar um JWT antes de ele expirar? Não diretamente, porque ele é stateless. Você mitiga isso com expiração curta, uma denylist de tokens revogados ou apoiando a revogação no refresh token, que é rastreado no servidor.

Onde devo guardar o JWT no navegador? Cookies HttpOnly e Secure reduzem o risco de roubo via XSS, mas exigem cuidado com CSRF. localStorage é mais simples, porém vulnerável a XSS. Não existe opção perfeita; a escolha depende do modelo de ameaça da aplicação.

Qual deve ser o tempo de expiração? Para access tokens, minutos (5 a 15 é comum). A vida longa fica por conta do refresh token, que pode durar dias ou semanas, mas com revogação possível.

Conclusão

O JWT é um formato elegante para transportar identidade de forma compacta, verificável e sem estado no servidor, o que o torna ideal para APIs e arquiteturas distribuídas. Sua força está na assinatura criptográfica, não no embaralhamento — por isso o conteúdo é público e nunca deve guardar segredos. Use expiração curta, valide todos os claims relevantes, fixe os algoritmos aceitos, apoie a revogação em refresh tokens e tenha uma estratégia clara para quando precisar invalidar acessos. Dominando essas práticas, você aproveita o melhor do JWT sem cair nas armadilhas que comprometem aplicações reais.

Referências

  • Jones, M., Bradley, J., Sakimura, N. (2015). RFC 7519: JSON Web Token (JWT). IETF.
  • Jones, M., Bradley, J., Sakimura, N. (2015). RFC 7515: JSON Web Signature (JWS). IETF.
  • Hardt, D. (2012). RFC 6749: The OAuth 2.0 Authorization Framework. IETF.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly