12k
All articles

Cookies vs localStorage para Autenticação com JWT

Cookies ou localStorage para autenticação JWT: compare riscos de XSS e CSRF, cookies HttpOnly, Secure, SameSite e padrões modernos de tokens.

OpenReplay Team
OpenReplay Team
Cookies vs localStorage para Autenticação com JWT

Você construiu seu fluxo de autenticação, os JWTs estão funcionando, e agora você se depara com a mesma questão que todo desenvolvedor frontend eventualmente enfrenta: onde eu realmente coloco esse token? A resposta importa mais do que a maioria dos tutoriais sugere, e o conselho comum — “use apenas cookies HttpOnly” — ignora tradeoffs reais que você precisa entender.

A seguir, apresentamos uma análise clara de ambas as opções, o que cada uma realmente protege e como aplicações modernas lidam com isso na prática.

Principais Conclusões

  • localStorage é totalmente acessível a qualquer JavaScript em execução na sua página, tornando-o vulnerável ao roubo de tokens via XSS.
  • Cookies HttpOnly bloqueiam completamente o acesso via JavaScript, mas introduzem risco de CSRF, que os atributos SameSite e Secure mitigam.
  • O padrão moderno armazena tokens de acesso de curta duração em memória e tokens de atualização em cookies HttpOnly, Secure e SameSite.
  • As diretrizes da OWASP e do OAuth para aplicações baseadas em navegador desaconselham o armazenamento de tokens de longa duração em localStorage.
  • A escolha correta depende do seu modelo de ameaças, do controle que você tem sobre o backend e se sua API exige cabeçalhos Authorization.

O Que Está Realmente em Jogo no Armazenamento de JWTs

O local onde você armazena um JWT determina quais vetores de ataque se aplicam à sua aplicação. As duas principais ameaças são:

  • XSS (Cross-Site Scripting): JavaScript malicioso executado no contexto da sua aplicação.
  • CSRF (Cross-Site Request Forgery): Induzir o navegador do usuário a realizar requisições autenticadas não intencionais.

Nenhuma das opções de armazenamento elimina ambos os riscos simultaneamente. O objetivo é entender qual risco você está aceitando e como mitigá-lo.

localStorage: Conveniente, mas Acessível via JavaScript

Armazenar um JWT no localStorage é simples. Você o escreve, lê e o anexa manualmente aos cabeçalhos Authorization: Bearer. Funciona bem com APIs que esperam esse formato de cabeçalho.

O problema é que o localStorage é totalmente acessível a qualquer JavaScript em execução na sua página. Se um atacante conseguir injetar um script — por meio de uma vulnerabilidade em uma dependência, um CDN comprometido ou uma falha de XSS no seu próprio código — ele poderá ler o token diretamente e exfiltrá-lo. A OWASP desaconselha explicitamente o armazenamento de identificadores de sessão no localStorage por esse motivo.

Isso não é teórico. Aplicações web modernas carregam dezenas de scripts de terceiros, e cada um representa uma superfície de ataque potencial.

Cookies HttpOnly: Melhor Resistência a XSS, Novas Considerações

Um cookie HttpOnly não pode ser lido por JavaScript de forma alguma. Mesmo que um atacante execute código na sua página, ele não conseguirá extrair o valor do token. Isso representa uma melhoria significativa.

Porém, cookies introduzem exposição a CSRF. Os navegadores anexam automaticamente cookies às requisições correspondentes, incluindo aquelas disparadas por sites maliciosos de terceiros.

Três atributos de cookie trabalham em conjunto para fechar essa brecha:

  • HttpOnly — bloqueia completamente o acesso via JavaScript.
  • Secure — transmite o cookie somente via HTTPS.
  • SameSite — controla quando os cookies são enviados em requisições entre origens diferentes.

Para o SameSite, os navegadores modernos adotam Lax como padrão quando o atributo não está definido, o que bloqueia cookies em sub-requisições entre origens (como POSTs de outra origem), mas os permite em navegações de nível superior. Strict é mais conservador e impede o envio do cookie em qualquer requisição entre origens, incluindo navegações de nível superior. Sempre defina esse atributo explicitamente em vez de depender dos padrões do navegador. O suporte ao SameSite é excelente nos navegadores modernos e pode ser verificado no Can I Use.

Com SameSite=Strict ou Lax corretamente configurado, o risco de CSRF é substancialmente reduzido para a maioria das configurações de autenticação no mesmo domínio. Para endpoints sensíveis que alteram estado, combine isso com um token anti-CSRF para uma defesa em profundidade.

O Padrão Utilizado pela Maioria das Aplicações Modernas

Muitas aplicações em produção dividem o problema da seguinte forma:

  1. Tokens de acesso de curta duração armazenados na memória JavaScript (uma variável em nível de módulo ou estado React).
  2. Tokens de atualização armazenados em um cookie HttpOnly, Secure e SameSite.

O token de acesso desaparece ao fechar a aba ou atualizar a página, mas uma chamada silenciosa ao endpoint /refresh recupera um novo token usando o cookie. O token de acesso nunca toca o armazenamento persistente, e o token de atualização nunca é legível via JavaScript.

Essa abordagem está alinhada com as diretrizes atuais para aplicações baseadas em navegador que utilizam OAuth 2.0 com PKCE (Authorization Code Flow com PKCE), que é o que as diretrizes do OAuth 2.0 para Aplicações Baseadas em Navegador recomendam. Se você estiver trabalhando com OpenID Connect (OIDC), o mesmo padrão se aplica — mantenha os ID tokens e tokens de atualização fora do localStorage.

Lista de Verificação de Segurança

Antes de publicar, verifique:

  • Flag HttpOnly definida em qualquer cookie que armazene tokens.
  • Flag Secure habilitada (HTTPS obrigatório).
  • SameSite explicitamente definido como Strict ou Lax.
  • Tokens de acesso com curta duração, tipicamente medida em minutos e não em horas.
  • Cabeçalhos de Content Security Policy configurados.
  • Nenhum JWT de longa duração armazenado no localStorage.

Escolhendo a Abordagem Correta para Sua Aplicação

Não existe uma resposta universal. Se você controla seu backend e serve sua aplicação a partir do mesmo domínio, cookies HttpOnly com configuração adequada de SameSite são o padrão mais seguro. Se você está integrando com uma API de terceiros que exige cabeçalhos Authorization e não pode definir cookies no lado do servidor, o armazenamento em memória com curta expiração é uma alternativa razoável — apenas nunca persista tokens de longa duração no localStorage.

Conclusão

JWTs de longa duração no localStorage são exatamente o que as diretrizes de segurança atuais desaconselham de forma consistente. Cookies HttpOnly com os atributos Secure e SameSite oferecem o padrão mais robusto para a maioria das configurações no mesmo domínio, enquanto o armazenamento em memória combinado com um cookie de token de atualização cobre os casos mais complexos. Uma vez que você compreende o modelo de ameaças — XSS de um lado, CSRF do outro — a escolha correta para sua aplicação se torna um tradeoff sobre o qual você pode raciocinar com clareza, em vez de uma suposição.

Perguntas Frequentes

Posso usar sessionStorage em vez de localStorage para JWTs?

O sessionStorage compartilha a mesma vulnerabilidade do localStorage: qualquer JavaScript em execução na página pode lê-lo. A única diferença é que o sessionStorage é limpo quando a aba é fechada. Isso reduz a janela de exposição, mas não protege contra XSS. Para armazenamento de tokens, trate o sessionStorage com o mesmo cuidado que o localStorage e evite armazenar tokens de longa duração nele.

Ainda preciso de proteção contra CSRF se usar cookies SameSite=Strict?

O SameSite=Strict impede que cookies sejam enviados em requisições entre origens, o que bloqueia a maioria dos padrões de ataque CSRF. No entanto, para endpoints de alto valor que alteram estado, adicionar um token anti-CSRF oferece uma defesa em profundidade. O SameSite é aplicado pelo navegador, portanto clientes mais antigos ou casos extremos incomuns podem não respeitá-lo. O padrão de double-submit token continua sendo uma salvaguarda sensata.

Qual deve ser a duração real da expiração do token de acesso?

Um intervalo comum é de 5 a 15 minutos. Curto o suficiente para que um token roubado tenha valor limitado, mas longo o suficiente para evitar sobrecarga no seu endpoint de atualização. Combine isso com um token de atualização de maior duração (horas a dias) em um cookie HttpOnly. Se sua aplicação lida com operações sensíveis, como pagamentos, prefira expiração mais curta e exija reautenticação para ações críticas.

E se minha API aceitar apenas cabeçalhos Authorization e eu não puder usar cookies?

Armazene o token de acesso na memória JavaScript — uma variável de módulo, estado React ou um closure — em vez de no localStorage. Mantenha-o com curta duração e atualize-o por meio de um endpoint de backend quando possível. Se você precisar persistir algo entre recarregamentos, direcione o fluxo de atualização pelo seu próprio backend, que mantém a credencial de longa duração no lado do servidor, e nunca exponha tokens de longa duração ao armazenamento do cliente.

Open-source session replay

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.