Pular para o conteúdo principal

JWT vs tokens de sessão

Sessões mantêm a verdade no servidor: um token opaco de 16–32 bytes indexa um armazenamento que você controla, então a revogação é instantânea e barata. JWTs carregam a verdade no próprio token — assinado, stateless, verificável em qualquer lugar — mas revogar significa esperar a expiração. Use sessões para logins de um único aplicativo; JWTs quando muitos serviços precisam verificar identidade sem um armazenamento compartilhado.

Só local

JWT vs tokens de sessão: explicação

Os dois designs resolvem o mesmo problema: requisições HTTP chegam sem memória, então cada requisição precisa de algo que diga “esta é a mesma pessoa que fez login”. Uma sessão clássica entrega ao cliente um token opaco — uma string aleatória que aponta para estado no servidor. Um JWT (RFC 7519) entrega ao cliente o próprio estado: um pacote assinado de claims que o servidor verifica sem consultar nada. Um guarda a verdade no servidor; o outro a registra em cartório e a envia junto.

Essa diferença — onde a verdade vive — se propaga por todo trade-off aqui. Um JWT se verifica com uma checagem de assinatura, então qualquer serviço que tenha a chave pode autenticar uma requisição sem armazenamento compartilhado; o preço é que o token continua válido até seu claim exp, então “faça logout agora” e “bloqueie este usuário agora” não têm implementação natural. Um token de sessão não vale nada sem seu armazenamento, então revogar é um delete — mas todo serviço que verifica precisa alcançar esse armazenamento.

Nenhum lado está vencendo, e sistemas maduros rotineiramente rodam os dois — cookies de sessão opacos para o aplicativo voltado a pessoas, JWTs de vida curta entre serviços. As seções abaixo comparam revogação, armazenamento, tamanho e superfície de ataque; o decodificador linkado ao longo do texto permite inspecionar tokens reais sem que nada seja enviado.

Antes de confiar em qualquer token em produção, desdobre um com o decodificador de JWT — ele decodifica header e payload localmente e nunca pede uma chave.

Para uma anatomia segmento por segmento, o guia de estrutura do JWT percorre header, payload e assinatura com exemplos reais.

Duas formas, duas filosofias

Uma sessão de servidor é um ponteiro. O token — 16 a 32 bytes aleatórios, normalmente definido como cookie — não carrega significado; todo significado vive no armazenamento: Redis, um banco de dados, até a memória do processo. Essa indireção é a funcionalidade. O registro pode conter qualquer coisa, mudar qualquer coisa ou sumir, e os clientes nunca percebem, segurando apenas um ponteiro.

Um JWT é uma declaração. Três segmentos base64url — header, payload, assinatura — carregam os claims: sub para o usuário, iss para o emissor, exp para a expiração, mais claims personalizados. A assinatura (HMAC com um segredo compartilhado, ou RSA/ECDSA com um par de chaves) prova que a declaração está intacta e veio de quem tem a chave. Verificar não exige consulta; confiar exige apenas a chave.

Revogação: o trade-off que decide a maioria dos projetos

Com sessões, “encerrar sessão” e “banir usuário” são operações no armazenamento: apague o registro, e a próxima requisição falha. Com JWTs, o token continua criptograficamente válido até o exp, não importa o que o servidor emissor agora deseje — o ponto do stateless é que a verificação nunca confere de volta. As mitigações são bem conhecidas e todas reintroduzem estado: expiração curta com rotação de refresh token, denylists por jti, ou um claim de versão incrementado no logout. Cada uma reconstrói uma fatia do armazenamento do qual você abriu mão.

O enquadramento honesto: sessões tornam a revogação grátis, mas cobram uma consulta ao armazenamento em cada requisição; JWTs a transformam em uma decisão de design que você paga na hora do logout. Se “encerrar sessão em todo lugar, imediatamente” é um requisito, sessões ou rotação rigorosamente gerenciada estão no seu futuro.

Armazenamento e superfície de ataque: cookies, localStorage, XSS e CSRF

Onde o token vive importa tanto quanto o que ele é. Um JWT estacionado no localStorage é legível por todo script da página, então uma única falha de XSS exfiltra uma credencial bearer válida até a expiração — sem como revogá-la pelo lado do servidor. Um token opaco em um cookie HttpOnly, Secure, SameSite fica invisível para o JavaScript, o que limita o raio de dano do XSS; o custo é a exposição a CSRF — o navegador anexa o cookie automaticamente — mitigada com SameSite e tokens CSRF.

JWTs podem viajar em cookies também (e muitas vezes devem), com as mesmas ressalvas de CSRF; um header Authorization escapa do CSRF, mas devolve o token ao alcance do JavaScript. Nenhum arranjo de armazenamento é seguro contra XSS — apenas arranjos que limitam o que o atacante pode roubar.

Tamanho, e o que um JWT realmente revela

Um JWT típico de login mede 800 bytes ou mais com alguns claims, e cada requisição o repete — enquanto cookies têm limite por volta de 4 KB por domínio. Um token de sessão opaco continua com 16–32 bytes de qualquer forma. Em escala, a diferença aparece em headers, proxies e logs.

O payload é base64url — codificação, não criptografia — então qualquer pessoa com o token pode ler todos os claims, incluindo dados pessoais e permissões. O JWE existe para tokens criptografados e quase nunca é usado. Cole um token real no decodificador abaixo: header e payload se desdobram no seu navegador, a assinatura é exibida mas nunca verificada — verificar exige a chave secreta, que a ferramenta nunca pede.

AspectoSessão de servidorJWT (RFC 7519)
Onde a verdade viveArmazenamento no servidorDentro do token assinado
RevogaçãoInstantânea — apague o registroNo exp, ou com estado adicional
Tamanho do token16–32 bytes opacos~800 B+ com claims típicos
Custo de verificaçãoConsulta ao armazenamento por requisiçãoChecagem de assinatura, sem consulta
Autenticação entre serviçosRequer armazenamento de sessão compartilhadoQualquer serviço que tenha a chave
ExpiraçãoTTL no servidor, mutávelclaim exp, fixado na emissão
Armazenamento no navegadorCookie HttpOnly + SameSiteCookie, ou localStorage (risco de XSS)

Quando escolher cada um

Escolha sessões quando um único aplicativo atende ao login: sites renderizados no servidor, painéis administrativos, qualquer coisa em que logout instantâneo e gerenciamento de dispositivos valham uma consulta ao Redis. Escolha JWTs quando a verificação precisa acontecer em muitos lugares — microsserviços, aplicativos móveis, serviços entre equipes — e um armazenamento de sessão compartilhado para todos eles é o custo mais pesado.

A orientação se inverte com clareza. No instante em que você constrói uma denylist ou um serviço de rotação para tornar JWTs revogáveis, pergunte-se se um armazenamento de sessão não voltou escondido; no instante em que um armazenamento de sessão vira gargalo de uma dúzia de serviços, pergunte-se se JWTs de vida curta entre eles não seriam mais leves. Híbridos — um cookie opaco para o navegador, JWTs de serviço para serviço — não são um compromisso; são o estado final normal.

Perguntas frequentes

Posso decodificar um JWT aqui sem enviá-lo a lugar algum?

Sim. O decodificador roda inteiramente no seu navegador: header e payload são base64url puro, então se desdobram localmente sem nenhuma requisição sair da aba — a Content-Security-Policy do site bloqueia chamadas de saída. A assinatura é exibida para inspeção, mas nunca verificada: verificar exige a chave de assinatura, que a ferramenta nunca pede.

Um JWT é criptografado?

Não. A RFC 7519 define um token assinado: o payload é base64url — legível por qualquer pessoa que o tenha — e a assinatura só prova quem o emitiu e que ele não foi alterado. Criptografia é um perfil separado (JWE) que sistemas reais raramente usam. Trate todo claim como público: identificadores e papéis são ok; segredos e dados pessoais, não.

Como revogar um JWT imediatamente?

Você não consegue só com o token — esse é o preço do stateless. Sistemas reais adicionam estado de volta: janelas de exp curtas com rotação de refresh token para tokens roubados morrerem rápido, uma denylist indexada pelo claim jti, ou um claim de versão de token incrementado no logout. As três são um armazenamento de sessão com etapas extras.

Cookies HttpOnly são mais seguros que localStorage para tokens?

Contra XSS, sim. O localStorage é legível por todo script da página, então uma única injeção rouba um token bearer válido até a expiração; um cookie HttpOnly fica invisível para o JavaScript, então a mesma injeção não consegue levá-lo. O custo do cookie é o CSRF, tratado com SameSite e tokens CSRF. O HttpOnly não derrota o XSS — ele tira o token do espólio.

Por que microsserviços preferem JWTs a sessões?

Porque a verificação se torna local. Com RS256, cada serviço guarda a chave pública e valida tokens sem chamada a um armazenamento de sessão compartilhado; um serviço novo sobe com uma chave, não com uma integração. Sessões opacas deixam todos os serviços à mercê da disponibilidade de um único armazenamento. Essa independência é o que o stateless compra — a revogação é a conta.

Quão grande um JWT pode ficar antes de virar problema?

Fique de olho nos cookies primeiro: os navegadores os limitam por volta de 4 KB por domínio, e um JWT cheio de claims pode se aproximar disso e quebrar requisições de vez. Mesmo abaixo do limite, um token de ~800 bytes viaja em cada requisição, enquanto um token de sessão opaco continua com 16–32 bytes de qualquer forma. Mantenha os claims enxutos: identidade e expiração, não dados de perfil.

Onde posso ler os padrões citados aqui?