🔐 Por que você (ainda) está salvando seu JWT no localStorage?
Quando comecei a trabalhar com autenticação em aplicações frontend, tudo parecia muito simples: o usuário faz login, a API retorna um JWT, e eu salvo o token no localStorage. Fácil, rápido e funcional. Até que os primeiros problemas começaram a surgir.
Se você ainda está usando essa abordagem, este artigo é um convite para repensar sua estratégia de segurança. Vou te mostrar os riscos reais e apresentar soluções mais profissionais.
☠️ O problema do localStorage: o alvo favorito dos XSS
O localStorage armazena dados de forma persistente e acessível via JavaScript. Isso significa que qualquer script rodando no seu domínio — mesmo um malicioso injetado via ataque XSS (Cross-Site Scripting) — pode acessar esse conteúdo.
🧪 Exemplo teórico:
Imagine que você carrega um widget de comentários de terceiros, ou uma lib que tem uma falha de segurança. Um atacante pode injetar um script no navegador do seu usuário e enviar o token para um servidor externo.
Agora o invasor tem o JWT e pode usá-lo para autenticar como esse usuário em outra máquina.
Repare: o problema não é o JWT em si — é onde ele está salvo.
🍪 Cookies com HttpOnly: menos conveniência, mais segurança
Uma alternativa muito mais segura é armazenar o token (idealmente o refresh token) em cookies com as flags HttpOnly e Secure ativadas.
✅ Benefícios:
HttpOnly: impede acesso ao cookie viadocument.cookie, protegendo contra XSS.Secure: só permite envio via HTTPS, protegendo contra man-in-the-middle.- Os cookies são enviados automaticamente a cada requisição feita ao mesmo domínio, sem depender de
localStorageou headers manuais.
⚙️ Exemplo prático (backend):
Set-Cookie: refresh_token=abc.def.ghi; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=604800
⚙️ Exemplo prático (frontend com fetch):
fetch("/api/profile", {
method: "GET",
credentials: "include"
});
Esse modelo é o preferido para aplicações SPA (Single Page Applications) modernas com frontend separado, e também para SSR (Next.js, Nuxt, etc.).
🔁 Token Refresh: manter sessões seguras e contínuas
É uma má ideia manter JWTs válidos por muito tempo. Em vez disso:
- Use um access token de curta duração (ex: 15 minutos).
- Use um refresh token HttpOnly com duração maior (ex: 7 dias).
- Crie um endpoint de refresh, que retorna um novo access token.
Assim, mesmo que um access token vaze, ele expira rapidamente. E o refresh token, estando em cookie HttpOnly, está bem mais protegido.
🧠 Redis + WebSocket: sessões seguras e reativas em tempo real
Agora, se você está lidando com múltiplos dispositivos, jogos online, chats, painéis administrativos, ou deseja um controle completo sobre sessões (como revogar tokens, limitar por IP/dispositivo, etc), o ideal é um modelo de sessão centralizada.
🛠️ Como funciona:
- O frontend recebe um
session_idapós login (salvo em cookie ou memória). - O backend mantém essa sessão no Redis, com TTL (tempo de expiração), IP, e dados do usuário.
- Toda requisição/autenticação vai até o Redis verificar a validade da sessão.
- Pode-se integrar com WebSocket para reconexões, eventos em tempo real, ou logout remoto.
💡 Vantagens:
- Permite invalidar sessões específicas a qualquer momento.
- Pode limitar sessões por dispositivo, IP ou localização.
- Ideal para apps com notificações em tempo real e escalabilidade horizontal.
🔐 Boas práticas complementares de segurança
Independente da abordagem escolhida, algumas boas práticas devem sempre ser aplicadas:
✅ CSP (Content Security Policy)
Evita execução de scripts externos maliciosos. Reduz risco de XSS.
Content-Security-Policy: default-src 'self';
✅ SameSite Cookies
Evita ataques CSRF. Use SameSite=Strict ou Lax.
✅ CORS bem configurado
Permite acesso apenas de domínios confiáveis.
Access-Control-Allow-Origin: https://meusite.com
✅ Expiração e rotação de tokens
Tokens curtos e que expiram rápido são mais seguros.
✅ Logout de todos os dispositivos
Permita logout global em sessões armazenadas no Redis.
🧩 Conclusão: segurança é arquitetura, não só criptografia
Autenticação não é só um login e um JWT. É uma peça essencial da arquitetura de segurança de qualquer aplicação moderna.
Resumo final:
| Abordagem | Segurança | Controle | Complexidade |
|---|---|---|---|
| localStorage | ❌ Alta vulnerabilidade (XSS) | Baixo | Baixa |
| Cookies HttpOnly | ✅ Muito seguro | Médio | Médio |
| Sessões em Redis | ✅ Altamente seguro | Alto | Alta |
💬 Se você trabalha com autenticação em aplicações web, pense sempre como um atacante. Onde eu atacaria? Como eu roubaria um token? E como eu mitigaria isso?
É essa mentalidade que diferencia quem apenas escreve código de quem projeta sistemas.
Se curtiu, compartilha com alguém que ainda guarda JWT no localStorage. Pode salvar uma aplicação! 😉