JWT vs Sessão: que mecanismo de autenticação escolher?

Autenticar um utilizador é saber, a cada pedido, quem ele é. Confrontam-se duas grandes famílias: as sessões de servidor, em que o servidor guarda o registo do utilizador ligado, e os JSON Web Tokens (JWT), em que a identidade viaja num token assinado transportado pelo cliente. A escolha influencia a segurança, a capacidade de escalar e a facilidade de desligar um utilizador. Eis como decidir consoante o seu contexto.

As sessões de servidor (stateful)

Com uma sessão, o servidor cria um identificador de sessão na ligação, armazena os dados associados (identidade, perfis, carrinho) do lado do servidor (memória, Redis, base de dados) e devolve ao navegador um cookie que contém apenas esse identificador. A cada pedido, o servidor lê o cookie, recupera a sessão e sabe quem está a falar.

  • Estado do lado do servidor: a fonte de verdade permanece no servidor, o cliente transporta apenas uma referência opaca.
  • Revogação imediata: eliminar a sessão do lado do servidor desliga instantaneamente o utilizador.
  • Cookie: transmitido automaticamente pelo navegador, idealmente em HttpOnly, Secure e SameSite.

Os JSON Web Tokens (stateless)

Um JWT é um token autossuficiente composto por três partes codificadas em base64url e separadas por pontos: um header, um payload (as claims, por exemplo o identificador do utilizador e a expiração) e uma assinatura. O servidor assina o token na ligação; depois basta-lhe verificar a assinatura para confiar no conteúdo, sem armazenar nada.

  • Sem estado: toda a informação necessária está no token, o servidor não precisa de memória partilhada.
  • Verificável em qualquer lado: qualquer serviço que conheça a chave pode validar o token, prático para as arquiteturas distribuídas e o SSO.
  • Ferramentas: pode inspecionar um token com o nosso descodificador JWT, controlar a sua assinatura com o verificador JWT ou forjar um com o gerador JWT.

Tabela comparativa

Critério Sessão de servidor JWT
EstadoStateful (armazenado no servidor)Stateless (transportado pelo cliente)
Armazenamento no servidorNecessário (Redis, base de dados)Nenhum
RevogaçãoImediataDifícil antes da expiração
Escalabilidade horizontalNecessário store partilhadoNativa
Tamanho transmitidoPequeno (um identificador)Maior (claims assinadas)
Entre domínios / SSORestritivoAdaptado
Superfície XSSReduzida se cookie HttpOnlyElevada se armazenado em localStorage

Segurança: XSS, CSRF e revogação

Ambas as abordagens são seguras se forem bem implementadas, mas os seus riscos diferem.

  • XSS: um cookie de sessão HttpOnly é inacessível ao JavaScript, portanto protegido do roubo por injeção. Um JWT armazenado em localStorage é, pelo contrário, legível por qualquer script, o que o torna um alvo de eleição. Armazenar o JWT num cookie HttpOnly anula esta vantagem do JWT mas reintroduz o risco CSRF.
  • CSRF: os cookies são enviados automaticamente, portanto vulneráveis ao CSRF sem proteção (atributo SameSite, token anti-CSRF). Um JWT enviado manualmente no cabeçalho Authorization não é afetado.
  • Revogação: é o ponto fraco do JWT. Como é autossuficiente, não é possível invalidá-lo antes da sua expiração sem reintroduzir um estado de servidor (lista de revogação, blacklist). Uma sessão elimina-se instantaneamente.

Escalabilidade e arquitetura

Num único servidor, as sessões são triviais. Assim que reparte a carga por várias instâncias, cada instância tem de aceder às sessões: é preciso um store partilhado (Redis) ou sticky sessions. O JWT brilha aqui, porque qualquer instância valida o token sem chamada de rede nem armazenamento comum.

  • Microsserviços: um JWT propaga a identidade de um serviço para outro sem base centralizada.
  • APIs públicas e móveis: o JWT evita a gestão de cookies do lado do cliente nativo.
  • Monólito clássico: a sessão continua mais simples e mais segura por omissão.

Quando escolher um ou outro

Escolher as sessões quando

  • Desenvolve uma aplicação web clássica com renderização no servidor
  • A revogação imediata é crítica (banca, saúde, back-office)
  • Pretende a solução mais segura por omissão, com menos armadilhas
  • A sua infraestrutura suporta um store de sessões partilhado sem dificuldade

Escolher o JWT quando

  • Expõe uma API consumida por SPA, dispositivos móveis ou terceiros
  • Tem uma arquitetura de microsserviços ou SSO entre domínios
  • Precisa de escalar horizontalmente sem store partilhado
  • Aceita gerir a expiração curta e a renovação dos tokens

Recomendação

Para a maioria das aplicações web, as sessões de servidor continuam a ser a escolha mais segura e mais simples: revogação imediata, cookie HttpOnly e zero gestão de token do lado do cliente. Reserve o JWT para os casos em que a sua ausência de estado traz valor real: API stateless, dispositivos móveis, microsserviços, SSO.

Se optar pelo JWT, mantenha um tempo de vida curto (alguns minutos) associado a um refresh token armazenado em cookie HttpOnly, e preveja uma lista de revogação para os casos sensíveis. Combina assim o melhor dos dois mundos.

Perguntas frequentes

Um JWT é cifrado?

Não, por omissão um JWT é apenas assinado, não cifrado. O seu payload é codificado em base64url e legível por quem o intercetar. Nunca coloque dados sensíveis em claro num JWT. Para cifrar o conteúdo, é preciso recorrer ao JWE (JSON Web Encryption).

Onde armazenar um JWT do lado do cliente?

O mais seguro é um cookie HttpOnly, Secure e SameSite, que protege do roubo por XSS. O localStorage é mais simples mas expõe o token a qualquer script malicioso. Evite-o para tokens com privilégios elevados.

Como desligar um utilizador com um JWT?

Como o token é autossuficiente, a desconexão real exige aguardar a sua expiração ou manter uma lista de revogação do lado do servidor. É por isso que se utilizam tempos de vida curtos e um refresh token que se pode, esse sim, revogar.

É possível combinar sessões e JWT?

Sim, é uma prática comum: um access token JWT de tempo de vida curto para as chamadas de API, e um refresh token gerido como uma sessão (armazenado e revogável do lado do servidor) para renovar o access token.