Anatomia e Melhores Práticas de Segurança com JWT (JSON Web Tokens)
Na era dos microsserviços, SPAs (Single Page Applications) e aplicativos móveis, o protocolo JWT (JSON Web Token - RFC 7519) tornou-se a escolha padrão para autenticação e autorização sem estado (stateless). Em vez de manter sessões pesadas salvas em memória de servidor (como Redis ou Memcached) para cada usuário logado, o servidor emite uma credencial compacta e criptograficamente assinada que o cliente anexa em requisições subsequentes.
No entanto, a facilidade de adoção do JWT frequentemente cria uma falsa sensação de segurança. Equívocos conceituais — como acreditar que um JWT esconde dados confidenciais ou salvá-lo sem critérios no localStorage do navegador — abrem portas para vazamento de credenciais e invasões via XSS. Neste artigo técnico, dissecamos a anatomia do token e as práticas essenciais para proteger suas aplicações.
1. A Estrutura dos Três Blocos (Header.Payload.Signature)
Um JWT consiste visualmente em uma longa string com três partes delimitadas por pontos (.):
A. Cabeçalho (Header) - Em Vermelho
Contém os metadados do token, especificando o tipo (typ: "JWT") e o algoritmo criptográfico utilizado para a assinatura (ex: HS256 para HMAC-SHA256 ou RS256 para RSA com par de chaves pública/privada).
B. Carga Útil (Payload) - Em Roxo
Contém as chamadas Claims (declarações sobre uma entidade, geralmente o usuário) e metadados contextuais. Importante: O payload não é criptografado, é apenas codificado em Base64Url! Qualquer pessoa que intercepte a string pode decodificá-la instantaneamente. Portanto, nunca coloque senhas, chaves de API ou dados de cartão de crédito no payload de um JWT.
As claims são divididas em:
- Claims Registradas (RFC 7519): Recomendações padronizadas como
sub(ID do usuário),iss(emissor),exp(timestamp UNIX de expiração),iat(data de emissão) eaud(audiência). - Claims Públicas ou Privadas: Dados de contexto do seu sistema, como
role: "admin"outenantId: "empresa-123".
C. Assinatura (Signature) - Em Azul
Garante a integridade do token. É calculada pegando o cabeçalho codificado, juntando com a carga útil codificada e aplicando o algoritmo definido no Header com a chave secreta do servidor:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)
2. As Vulnerabilidades Críticas Mais Comuns
1. A Falha do Algoritmo "none" (CVE-2015-9235)
A especificação original do JWT permite que o campo alg receba o valor none para tokens que teoricamente não necessitam de verificação de assinatura. No passado, bibliotecas negligentes confiavam no header vindo do cliente sem validação estrita. Um atacante podia alterar o payload para "role": "admin", definir alg: "none", remover a assinatura e o servidor aceitava a requisição com privilégios máximos. Regra: Rejeite explicitamente tokens com alg: "none" em seus gateways.
2. Chaves Secretas Fracas (Ataques de Dicionário)
Tokens assinados com algoritmos simétricos (HS256) usam uma chave compartilhada. Se a chave for simples (como secret, minha-chave-123 ou jwt_token), invasores com ferramentas como Hashcat ou John the Ripper conseguem quebrar o hash offline em segundos e forjar novos tokens arbitrários. Utilize sempre segredos aleatórios de pelo menos 256 bits gerados por geradores criptograficamente seguros (CSPRNG).
3. Armazenamento Seguro no Frontend: LocalStorage vs Cookie HttpOnly
Onde armazenar o token após o login no navegador? Essa é uma das decisões arquiteturais mais cruciais para a segurança do usuário:
| Mecanismo | Vulnerabilidade a XSS | Vulnerabilidade a CSRF | Veredito de Segurança |
|---|---|---|---|
| localStorage / sessionStorage | Altíssima (Inseguro) | Imune a CSRF puro | Qualquer script malicioso injetado rouba o token. |
| Cookie HttpOnly; Secure; SameSite=Strict | Imune (JavaScript bloqueado) | Protegido com SameSite | Padrão recomendado pela OWASP para autenticação web. |
4. A Arquitetura Recomendada: Access Token Curto + Refresh Token Rotativo
A melhor abordagem moderna combina as duas frentes:
- Access Token: Possui validade extremamente curta (ex: 10 a 15 minutos). Fica armazenado apenas na memória volátil da aplicação JavaScript (uma variável no estado do React, Vue ou Angular). Se a aba fechar, ele é descartado.
- Refresh Token: Possui validade maior (ex: 7 a 30 dias). É enviado pelo servidor como um cookie
HttpOnly; Secure; SameSite=Strict. Quando o Access Token de 15 minutos expira, o cliente faz uma chamada silenciosa a/auth/refreshpara obter um novo token sem que o usuário perceba. - Revogação por Rotação: A cada uso do Refresh Token, o servidor invalida o token anterior e emite um novo. Se um refresh token já usado for apresentado novamente, o backend invalida imediatamente toda a família de sessões daquele usuário (indicativo de sequestro de credencial).
Perguntas Frequentes sobre JWT (FAQ)
Sobre o Autor: Alexandre Marcelino
Formado em Engenharia de Software, pós-graduando em Arquitetura de Software e com mais de 6 anos de experiência na área de tecnologia. Criador e mantenedor da Caixa do Dev. Conheça seus projetos no GitHub (@xandy139).