O que é autenticação por sessão e cookie
Na autenticação baseada em sessão, o back-end mantém um estado: após o login, ele cria uma sessão (um registro no servidor) e entrega ao cliente um identificador de sessão (session id) normalmente via cookie. Em cada requisição seguinte, o navegador envia automaticamente esse cookie; o servidor usa o session id para localizar a sessão e, a partir dela, identificar o usuário autenticado.
Componentes típicos:
- Cookie de sessão: armazena o session id no cliente (não deve armazenar dados sensíveis).
- Store de sessão: onde o servidor guarda o estado da sessão (ex.: userId, data de expiração, metadados).
- Middleware de sessão: lê o cookie, carrega a sessão e anexa o usuário ao contexto da requisição.
- Logout: invalida a sessão no store e remove/expira o cookie.
Modelo de sessão do zero: estrutura e fluxo
Estrutura mínima de uma sessão
Uma sessão pode ser representada por um registro com campos como:
id: identificador aleatório (session id).userId: referência ao usuário autenticado.createdAt,lastSeenAt: auditoria e renovação.expiresAt: expiração absoluta.revokedAt(opcional): marca de revogação.ip,userAgent(opcional): detecção de anomalias.
Passo a passo: criação de sessão no login
- Validar credenciais (ex.: email/senha).
- Gerar um session id criptograficamente seguro.
- Persistir a sessão no store associando
sessionIdaouserIde definindoexpiresAt. - Enviar cookie com o session id e atributos de segurança.
- Responder com dados não sensíveis (ex.: perfil básico) se necessário.
Passo a passo: autenticação em requisições subsequentes
- O navegador envia o cookie automaticamente.
- O middleware lê o cookie e extrai o session id.
- O servidor busca a sessão no store.
- Se existir e estiver válida (não expirada/revogada), anexa
req.user(ou equivalente) e permite seguir. - Se inválida, retorna
401e opcionalmente expira o cookie.
Passo a passo: invalidação no logout
- Identificar a sessão atual via cookie.
- Remover a sessão do store (ou marcar como revogada).
- Enviar um
Set-Cookiepara expirar o cookie no cliente.
Endpoints típicos e comportamento esperado
POST /login
Cria sessão e define cookie.
POST /login { email, password }Resposta (exemplo):
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
200 OK { user: { id, name } }GET /me
Requer sessão válida; retorna dados do usuário autenticado.
GET /meResposta (exemplo):
200 OK { id, name, email }POST /logout
Invalida sessão e expira cookie.
POST /logoutResposta (exemplo):
204 No ContentPseudocódigo: criação, leitura e invalidação de sessão
Gerando um session id seguro
O session id deve ser imprevisível. Use um gerador criptográfico e codifique em base64url/hex.
function generateSessionId() { bytes = cryptoRandomBytes(32) return base64urlEncode(bytes) }Store de sessão (interface)
Defina uma interface para trocar o backend de armazenamento sem reescrever o middleware.
interface SessionStore { get(sessionId): Session|null set(sessionId, session, ttlSeconds): void delete(sessionId): void }Login: criar sessão e setar cookie
function loginHandler(req, res) { { email, password } = req.body user = Users.findByEmail(email) if (!user || !verifyPassword(password, user.passwordHash)) { return res.status(401).json({ error: "invalid_credentials" }) } sessionId = generateSessionId() now = nowUtc() ttlSeconds = 60 * 60 * 24 * 7 // 7 dias session = { id: sessionId, userId: user.id, createdAt: now, lastSeenAt: now, expiresAt: now + ttlSeconds, userAgent: req.headers["user-agent"], ip: req.ip } SessionStore.set(sessionId, session, ttlSeconds) res.setCookie("sid", sessionId, { httpOnly: true, secure: true, sameSite: "Lax", path: "/", maxAge: ttlSeconds }) return res.json({ user: { id: user.id, name: user.name } }) }Middleware de sessão: carregar sessão e anexar usuário
function sessionMiddleware(req, res, next) { sid = req.cookies["sid"] if (!sid) { req.session = null req.user = null return next() } session = SessionStore.get(sid) if (!session) { // cookie pode estar stale; expira no cliente para reduzir ruído res.setCookie("sid", "", { path: "/", maxAge: 0 }) req.session = null req.user = null return next() } if (session.expiresAt < nowUtc()) { SessionStore.delete(sid) res.setCookie("sid", "", { path: "/", maxAge: 0 }) req.session = null req.user = null return next() } // opcional: sliding session (renovar lastSeen/TTL) session.lastSeenAt = nowUtc() // se store suportar, renove TTL aqui // SessionStore.set(sid, session, newTtlSeconds) req.session = session req.user = Users.findById(session.userId) return next() }Middleware de autenticação: bloquear rotas protegidas
function requireAuth(req, res, next) { if (!req.user) { return res.status(401).json({ error: "unauthenticated" }) } return next() }Logout: invalidar sessão
function logoutHandler(req, res) { sid = req.cookies["sid"] if (sid) { SessionStore.delete(sid) } res.setCookie("sid", "", { path: "/", maxAge: 0, httpOnly: true, secure: true, sameSite: "Lax" }) return res.status(204).send() }Armazenamento de sessão: memória vs banco vs cache (Redis)
1) Sessão em memória (in-process)
Como funciona: um mapa/dicionário no próprio processo do servidor (ex.: Map<sid, session>).
- Vantagens: simples, rápido, sem dependências externas.
- Desvantagens: não escala horizontalmente; ao subir múltiplas instâncias, cada uma tem seu próprio mapa. Se o balanceador enviar o usuário para outra instância, a sessão “some”. Reinícios derrubam todas as sessões.
- Implicações: para funcionar com múltiplas instâncias, exigiria sticky sessions no balanceador, o que reduz flexibilidade e pode complicar alta disponibilidade.
2) Sessão em banco de dados (relacional ou NoSQL)
Como funciona: tabela/coleção de sessões com índice por id e campos de expiração.
- Vantagens: persistência; funciona bem com múltiplas instâncias; facilita auditoria e revogação; backups e replicação podem ajudar em HA.
- Desvantagens: latência maior que memória/cache; alto volume de leitura por requisição pode pressionar o banco; exige limpeza de sessões expiradas (job/TTL).
- Implicações: em sistemas com muito tráfego, o banco pode virar gargalo; é comum usar cache para sessões ou migrar para store dedicado.
3) Sessão em cache distribuído (ex.: Redis)
Como funciona: chave-valor com TTL nativo (ex.: SET sid value EX ttl).
- Vantagens: baixa latência; TTL nativo simplifica expiração; adequado para múltiplas instâncias; bom para alto volume.
- Desvantagens: requer infraestrutura extra; precisa planejar persistência (RDB/AOF) conforme tolerância a perda; falhas podem derrubar autenticação se não houver HA.
- Implicações de escalabilidade: facilita escalar horizontalmente o app; o store centralizado mantém consistência de sessão entre instâncias.
- Implicações de alta disponibilidade: use Redis com replicação e failover (ex.: Sentinel/Cluster) e configure timeouts/retries para não travar requisições.
Tabela comparativa
| Store | Escala horizontal | Latência | Persistência | Complexidade | HA |
|---|---|---|---|---|---|
| Memória | Ruim (depende de sticky) | Muito baixa | Não | Baixa | Baixa |
| Banco | Boa | Média | Sim | Média | Média/Alta (com réplica) |
| Redis | Excelente | Baixa | Opcional | Média/Alta | Alta (com cluster/failover) |
Configuração segura de cookies de sessão
O cookie é o “portador” do session id. Se ele for roubado, um atacante pode sequestrar a sessão. Por isso, os atributos do cookie são parte central da segurança.
HttpOnly
- O que faz: impede acesso ao cookie via JavaScript (
document.cookie). - Por que importa: reduz impacto de XSS no roubo de sessão.
- Recomendação: habilitar sempre para cookie de sessão.
Secure
- O que faz: cookie só é enviado via HTTPS.
- Por que importa: evita vazamento em HTTP.
- Recomendação: habilitar em produção; em dev local pode exigir HTTPS local ou exceção controlada.
SameSite (Lax/Strict/None)
- O que faz: controla envio do cookie em navegação cross-site, mitigando CSRF.
- Lax: bom padrão para apps tradicionais; envia cookie em navegação de topo (ex.: clicar em link), mas bloqueia muitos POSTs cross-site.
- Strict: mais restritivo; pode quebrar fluxos legítimos (ex.: entrar via link externo e já estar logado).
- None: necessário para cenários cross-site (ex.: front-end em um domínio e API em outro) e exige Secure.
Path
- O que faz: restringe para quais caminhos o cookie é enviado.
- Recomendação: use
/se a sessão vale para toda a aplicação; use um path mais específico se quiser limitar escopo (ex.:/api).
Domain
- O que faz: define quais hosts recebem o cookie.
- Recomendação: evite definir
Domainquando não necessário (deixe host-only), reduzindo superfície. Se precisar compartilhar entre subdomínios (ex.:app.exemplo.comeapi.exemplo.com), configure cuidadosamente (ex.:Domain=exemplo.com).
Tempo de vida: Expires/Max-Age e sessão deslizante
- Cookie de sessão (sem Expires/Max-Age): tende a expirar ao fechar o navegador, mas não é garantia de segurança por si só.
- Cookie persistente (com Max-Age/Expires): permanece até expirar; melhora UX, aumenta janela de risco se roubado.
- TTL no store: deve existir mesmo se o cookie for persistente; o servidor precisa expirar sessões no lado dele.
- Sessão deslizante (sliding): renova TTL a cada uso; melhora conveniência, mas pode manter sessões vivas indefinidamente. Uma alternativa é combinar expiração absoluta (ex.: 7 dias) com expiração por inatividade (ex.: 30 min).
Exemplo de Set-Cookie recomendado
Set-Cookie: sid=BASE64URL_RANDOM; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800Cuidados práticos e armadilhas comuns
Não coloque dados sensíveis no cookie
O cookie deve conter apenas um identificador opaco (session id). Dados do usuário ficam no store.
Rotação de session id (mitigar fixation)
Após login bem-sucedido, gere um novo session id (não reaproveite um id pré-existente). Se houver sessão anônima antes do login, invalide-a e crie outra autenticada.
Revogação e múltiplas sessões
Decida se um usuário pode ter múltiplas sessões (vários dispositivos). Se quiser limitar, ao criar uma nova sessão você pode revogar as anteriores do mesmo usuário (ex.: manter apenas a mais recente) ou oferecer endpoint para listar e revogar sessões.
Limpeza de sessões expiradas
- Memória: varredura periódica para remover expiradas.
- Banco: job periódico (
DELETE WHERE expiresAt < now) e índice emexpiresAt. - Redis: TTL nativo já remove automaticamente, mas ainda é útil ter métricas e alertas.
Quando o front-end e a API estão em domínios diferentes
Se o cookie precisar ser enviado em contexto cross-site, normalmente será necessário SameSite=None; Secure e configuração correta de CORS com credenciais (ex.: permitir credentials e origin explícita). Além disso, o atributo Domain pode ser necessário para compartilhar entre subdomínios, mas deve ser usado com cautela.