Introdução: O que é um JSON Web Token?
JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define uma forma compacta e autocontida de transmitir informações com segurança entre partes como um objeto JSON. Essas informações podem ser verificadas e consideradas confiáveis porque são assinadas digitalmente. JWTs podem ser assinados usando um segredo (algoritmo HMAC) ou um par de chaves pública/privada (RSA ou ECDSA).
Os JWTs foram criados para resolver um desafio comum em sistemas distribuídos: como um servidor pode confiar em uma requisição de um cliente sem precisar consultar uma sessão no banco de dados? A resposta é codificar e assinar a identidade e as permissões do usuário diretamente em um token que o cliente carrega em cada requisição.
Hoje, os JWTs são a espinha dorsal da autenticação e autorização modernas:
- Single Sign-On (SSO): Um único login concede acesso a múltiplos serviços.
- Autenticação de API: Aplicações móveis e SPAs autenticam chamadas à API com um Bearer token.
- OAuth 2.0 / OpenID Connect: JWTs são usados como access tokens e ID tokens.
- Microsserviços: Serviços verificam a identidade do chamador sem um armazenamento central de sessões.
Estrutura do JWT: header.payload.signature
Um JWT é uma string de três partes codificadas em Base64url, separadas por pontos (.):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Parte 1 — Header (Cabeçalho) decodificado:
{
"alg": "HS256",
"typ": "JWT"
}
O cabeçalho especifica o algoritmo de assinatura (alg) e o tipo de token (typ).
Parte 2 — Payload (Carga útil) decodificado:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
O payload contém os claims — declarações sobre a entidade (geralmente um usuário) e metadados adicionais.
Parte 3 — Signature (Assinatura) (para HS256) calculada como:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
A assinatura garante que o token não foi adulterado. Nota crítica: O payload é apenas codificado em Base64url, não criptografado. Qualquer pessoa que obtiver o JWT pode decodificar e ler todos os claims.
Algoritmos de Assinatura: HS256, RS256 e ES256
HS256 (HMAC-SHA256) — Simétrico
Usa um único segredo compartilhado tanto para assinar quanto para verificar. Simples de implementar, mas o mesmo segredo deve ser conhecido pelo emissor e pelo verificador.
- Ideal para: Aplicações monolíticas ou serviços fortemente acoplados que compartilham um segredo.
- Risco: Se o segredo vazar, todos os tokens podem ser forjados.
RS256 (RSA-SHA256) — Assimétrico
Usa uma chave privada para assinar e uma chave pública para verificar. A chave privada permanece secreta; a pública pode ser compartilhada livremente via endpoint JWKS.
- Ideal para: Sistemas distribuídos, microsserviços e cenários onde múltiplos serviços verificam tokens.
- Tamanho de chave: Mínimo de 2048 bits para RSA recomendado.
ES256 (ECDSA-SHA256) — Assimétrico
Usa o Algoritmo de Assinatura Digital de Curva Elíptica com a curva P-256. Produz assinaturas menores que RS256 com segurança equivalente.
- Ideal para: Ambientes com restrições de desempenho ou largura de banda.
| Algoritmo | Tipo | Chave | Tamanho da assinatura | Caso de uso |
|---|---|---|---|---|
| HS256 | Simétrico | Segredo compartilhado | 32 bytes | Apps de único serviço |
| RS256 | Assimétrico | Par de chaves RSA | 256+ bytes | Sistemas distribuídos |
| ES256 | Assimétrico | Par de chaves EC | 64 bytes | APIs de alto desempenho |
Claims JWT: Registrados, Públicos e Privados
Claims são declarações sobre uma entidade (geralmente um usuário) e metadados codificados no payload.
Claims Registrados (definidos pela IANA)
| Claim | Nome completo | Descrição |
|---|---|---|
iss |
Issuer | Quem emitiu o token (ex: "auth.example.com") |
sub |
Subject | A quem o token se refere (ex: um ID de usuário) |
aud |
Audience | Para quem o token é destinado (identificador de serviço) |
exp |
Expiration | Timestamp Unix após o qual o token é inválido |
nbf |
Not Before | O token não deve ser usado antes deste timestamp |
iat |
Issued At | Timestamp Unix de quando o token foi emitido |
jti |
JWT ID | Identificador único para prevenir ataques de replay |
Claims Públicos
Definidos no Registro de Claims JWT da IANA. Exemplos: email, name, picture, roles.
Claims Privados
Claims personalizados acordados entre emissor e consumidor. Use nomes com namespace para evitar colisões:
{
"https://example.com/tenant_id": "acme-corp",
"https://example.com/roles": ["admin", "editor"]
}
Como Funciona o Fluxo de Autenticação JWT
- Login do usuário: O cliente envia credenciais (usuário + senha) ao servidor de autenticação.
- Emissão do token: O servidor valida as credenciais, cria um JWT assinado com os claims apropriados e o retorna ao cliente.
- Armazenamento do token: O cliente armazena o JWT (em memória, localStorage ou um cookie HttpOnly).
- Requisições autenticadas: Para cada requisição a um recurso protegido, o cliente inclui o JWT no cabeçalho
Authorization:Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - Verificação do token: O servidor extrai o token, verifica a assinatura, checa
expe concede ou nega acesso com base nos claims. - Renovação do token: Quando o access token expira, o cliente usa um refresh token de longa duração para obter um novo sem se reautenticar.
Cliente Servidor Auth Servidor de Recursos
| | |
|-- POST /login ---->| |
|<-- 200 + JWT ------| |
| | |
|-- GET /api (Authorization: Bearer JWT) ->|
| | verificar JWT |
|<----------- 200 + dados ---------------->|
Considerações de Segurança
O Payload NÃO É Criptografado
Codificação Base64url não é criptografia. Qualquer pessoa que tenha um JWT pode decodificar e ler todos os claims instantaneamente. Nunca inclua em um payload JWT:
- Senhas ou hashes de senhas
- Números de cartão de crédito ou dados financeiros
- Informações pessoais sensíveis (PII) além do necessário
- Chaves privadas ou segredos internos
Sempre Verifique a Assinatura
// Node.js — biblioteca jsonwebtoken
const jwt = require("jsonwebtoken");
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded.sub); // ID de usuário confiável
} catch (err) {
res.status(401).json({ error: "Token inválido ou expirado" });
}
Use Tempos de Expiração Curtos
Access tokens devem ter ciclos de vida curtos (15 minutos a 1 hora). Use refresh tokens para sessões longas e limitar o impacto de um token roubado.
Sempre Use HTTPS
Nunca transmita tokens por conexões não criptografadas. Um token interceptado é tão perigoso quanto uma senha roubada.
Vulnerabilidades Comuns
1. Ataque de Confusão de Algoritmo (CVE-2015-9235)
Um atacante muda o campo alg no cabeçalho de RS256 para HS256 e assina o token usando a chave pública do servidor como segredo HMAC. Um servidor mal implementado aceita o token forjado.
Mitigação: Especifique sempre o algoritmo esperado explicitamente no servidor:
jwt.verify(token, publicKey, { algorithms: ["RS256"] });
2. O Ataque do Algoritmo "none"
Se um servidor aceita alg: "none", um atacante pode criar tokens sem nenhuma assinatura.
Mitigação: Rejeite explicitamente tokens com alg: "none". Bibliotecas modernas corrigiram isso, mas versões antigas podem ser vulneráveis.
3. Segredos Fracos (HS256)
Um segredo HMAC curto ou fácil de adivinhar pode ser quebrado por força bruta offline com ferramentas como hashcat contra um token capturado.
Mitigação: Use um segredo aleatório criptograficamente seguro de pelo menos 256 bits:
openssl rand -hex 32
4. Validação de Claims Ausente
Não validar iss, aud ou nbf permite reutilização de tokens entre serviços ou uso antes do período válido.
5. Armazenamento Inseguro de Tokens
localStorage é acessível por qualquer JavaScript na página, tornando tokens vulneráveis a ataques XSS. Prefira cookies HttpOnly para tokens sensíveis.
JWT vs Tokens de Sessão: Sem Estado vs Com Estado
| Característica | JWT (Sem estado) | Token de sessão (Com estado) |
|---|---|---|
| Armazenamento | Lado do cliente | Lado do servidor (BD ou cache) |
| Escalabilidade | Excelente — sem consulta ao servidor | Requer armazenamento de sessão compartilhado |
| Revogação | Difícil — válido até exp |
Fácil — excluir do armazenamento |
| Tamanho do token | Maior por requisição | Token pequeno, dados no servidor |
| Microsserviços | Ideal — verificação independente | Requer infraestrutura compartilhada |
| Visibilidade | Qualquer um com o token pode ler | Dados ficam apenas no servidor |
Escolha JWT ao construir sistemas distribuídos, APIs REST sem estado ou aplicações móveis. Escolha sessões quando precisar de revogação imediata (ex: "sair de todos os dispositivos").
O Desafio da Revogação
Como JWTs são autocontidos, você não pode cancelar um token antes de expirar sem infraestrutura adicional:
- Expiração curta + refresh tokens: Access tokens expiram rapidamente; revogue refresh tokens no BD.
- Lista negra de tokens: Armazene
jtirevogados no Redis (reintroduz alguma gestão de estado). - Rotação de segredos: Rotacione o segredo de assinatura para invalidar todos os tokens (impacta todos os usuários).
OAuth 2.0 e OpenID Connect
OAuth 2.0
OAuth 2.0 é um framework de autorização que não exige um formato de token específico. Porém, JWTs são amplamente usados como access tokens OAuth porque são autocontidos e carregam escopos de autorização:
{
"iss": "https://auth.example.com",
"sub": "user_123",
"aud": "https://api.example.com",
"scope": "read:profile write:posts",
"exp": 1893456000
}
OpenID Connect (OIDC)
OpenID Connect é uma camada de identidade construída sobre o OAuth 2.0. Ele introduz o ID Token, que é sempre um JWT contendo claims de identidade sobre o usuário autenticado:
{
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"aud": "my-client-id.apps.googleusercontent.com",
"email": "[email protected]",
"name": "Jane Doe",
"exp": 1893456000,
"iat": 1893452400
}
O OIDC também define o endpoint JWKS (/.well-known/jwks.json), onde o servidor de autorização publica suas chaves públicas. Qualquer serviço pode buscar essas chaves para verificar tokens sem uma troca prévia de chaves.
Boas Práticas
- Use algoritmos assimétricos (RS256/ES256) para sistemas públicos — proteja a chave privada de assinatura enquanto qualquer um pode verificar com a chave pública.
- Defina valores
expcurtos para access tokens — 15 minutos é um padrão seguro e comum. - Sempre rotacione refresh tokens — emita um novo a cada uso e invalide o anterior.
- Valide todos os claims relevantes — verifique no mínimo
iss,aud,expenbf. - Especifique o algoritmo esperado explicitamente — nunca confie apenas no campo
algdo cabeçalho do token. - Nunca coloque dados sensíveis no payload — ele é codificado, não criptografado.
- Sempre use HTTPS — nunca transmita tokens por conexões não criptografadas.
- Armazene tokens com segurança — prefira cookies
HttpOnly,Secure,SameSite=Strictao localStorage. - Implemente uma estratégia de revogação de tokens — use lista negra ou tokens de curta duração com rotação de refresh tokens.
- Mantenha bibliotecas JWT atualizadas — novas versões incluem correções de segurança.
Perguntas Frequentes (FAQ)
P: Posso descriptografar o payload de um JWT? R: O payload de um JWT é codificado em Base64url, não criptografado — qualquer um pode lê-lo sem nenhuma chave. Se precisar de confidencialidade, use JWE (JSON Web Encryption, RFC 7516), que de fato criptografa o payload.
P: Qual é a diferença entre decode e verify?
R: decode apenas decodifica as partes em Base64url e retorna o JSON sem verificações de segurança. verify também valida a assinatura com sua chave e verifica claims temporais como exp. Sempre use verify em produção.
P: Como gerenciar a renovação de tokens com segurança? R: Emita um access token de curta duração (15–60 min) e um refresh token de longa duração (dias a semanas). Armazene refresh tokens no servidor para que possam ser revogados. Quando o access token expira, o cliente envia o refresh token a um endpoint dedicado para obter um novo access token.
P: Posso usar JWT tanto para autenticação quanto para autorização?
R: Sim. Inclua claims de identidade (quem é o usuário) e claims de autorização (o que ele pode fazer, ex: roles, scope) no mesmo token. Mantenha o tamanho do payload razoável.
P: O que acontece se meu segredo JWT vazar? R: Um atacante pode forjar tokens válidos para qualquer usuário. Rotacione o segredo imediatamente (isso invalida todos os tokens existentes), investigue a fonte do vazamento e force todos os usuários a se reautenticarem.
P: Devo usar HS256 ou RS256? R: Use RS256 (ou ES256) quando múltiplos serviços verificam tokens ou quando o verificador não é totalmente confiável. Use HS256 apenas quando o mesmo serviço emite e verifica tokens dentro de um limite de implantação estritamente controlado.
P: Qual o tamanho máximo de um JWT? R: Mantenha JWTs abaixo de 4 KB para evitar limites de tamanho de cabeçalhos HTTP e problemas de desempenho. Não incorpore grandes estruturas de dados no payload; referencie-as por ID.