Segurança de cookies: as flags Secure, HttpOnly e SameSite explicadas e configuradas
Cookies de sessão sem Secure, HttpOnly ou SameSite podem ser roubados ou mal usados. Como verificar cada flag e configurá-la.
Severidade típica: Média · Verificado pelo módulo da NovaPentest: Cookie Security
O que é
Cookies carregam o estado que mantém o usuário logado: identificadores de sessão, tokens de autenticação, tokens CSRF, preferências. Quem tem um cookie de sessão válido é, do ponto de vista da aplicação, aquele usuário. O header Set-Cookie permite ao servidor anexar atributos que limitam onde e como o navegador usa cada cookie.
O Secure diz ao navegador para enviar o cookie apenas em HTTPS. Sem ele, o cookie vai junto em qualquer requisição HTTP para o seu domínio, inclusive as disparadas por um link antigo ou por uma tag de imagem.
O HttpOnly esconde o cookie do JavaScript. O navegador continua enviando o cookie nas requisições, mas document.cookie não consegue lê-lo, então um script que acabe rodando na sua página não consegue copiar o valor.
O SameSite controla se o cookie é enviado em requisições que começam em outro site. Lax, que os navegadores modernos aplicam quando o atributo não é informado, retém o cookie em sub-requisições e envios de formulário entre sites, mas permite a navegação de nível superior por link. Strict retém o cookie em toda requisição entre sites. None envia sempre e exige Secure.
O tempo de vida também importa. Um cookie sem Expires nem Max-Age é um cookie de sessão e some quando o navegador fecha; um com validade longa continua válido no dispositivo pelo tempo que você permitir.
Juntos, esses atributos transformam um cookie acessível a qualquer página, rede ou script em um valor que só trafega por um canal criptografado até o seu próprio site.
Como é explorada
Um cookie de sessão sem Secure pode sair do navegador em texto claro. Se um usuário em um Wi-Fi compartilhado clicar em qualquer link http:// para o seu domínio, mesmo que ele redirecione em seguida para HTTPS, essa primeira requisição leva o cookie, e qualquer pessoa capaz de observar a rede pode reutilizá-lo para agir como o usuário até a sessão expirar.
Um cookie de sessão sem HttpOnly transforma qualquer injeção de script em tomada de conta. Se existir um bug de cross-site scripting em qualquer ponto do domínio, ou se um script de terceiros que você inclui for comprometido, o script pode ler o cookie e enviá-lo para fora. Com HttpOnly, o código injetado ainda pode causar problemas dentro da página, mas não leva a sessão embora.
Sem um SameSite explícito, você depende dos padrões dos navegadores, que variam entre fabricantes e versões. Um cookie que acaba se comportando como SameSite=None é enviado em requisições forjadas por outro site, então uma página que o usuário visita em outro lugar pode enviar um formulário para a sua aplicação com a sessão dele anexada, alterando configurações ou disparando ações que ele nunca pediu.
Cookies de longa duração estendem todos esses riscos no tempo. Um token de sessão válido por anos, guardado em um notebook compartilhado ou perdido, continua sendo uma credencial funcional muito depois de o usuário ter se esquecido dele.
Em todos os casos o usuário não faz nada de errado e não vê nada de estranho; o prejuízo recai sobre a conta e os dados dele.
Como detectar
O módulo Cookie Security da NovaPentest faz uma requisição GET para a URL alvo, segue redirecionamentos e analisa cada header Set-Cookie da resposta final. Para cada cookie, reporta a ausência de Secure como Média, a ausência de HttpOnly como Média e a ausência de SameSite como Baixa. Também marca como Baixa qualquer cookie cujo Max-Age, ou na falta dele o Expires, fique a mais de um ano no futuro. O módulo roda no scanner passivo gratuito em novapentest.com/scan.
A verificação tem limites claros. Ela só enxerga os cookies definidos na página inicial para um visitante anônimo; cookies de sessão emitidos depois do login, ou definidos em respostas intermediárias de redirecionamento, não são analisados. Ela não sabe quais cookies são sensíveis, então aponta falta de HttpOnly em cookies que precisam legitimamente de acesso via JavaScript, como alguns cookies de consentimento ou de analytics, e esses achados podem ser aceitos. Ela confere apenas se o SameSite está presente, então SameSite=None passa. Se a página não define cookies, nada é reportado.
Para conferir manualmente, rode curl -sI https://example.com e leia cada linha Set-Cookie; acrescente -L para seguir redirecionamentos. Repita depois do login passando a sua sessão com -b ou, melhor, abra as ferramentas de desenvolvedor do navegador, vá em Application (Chrome) ou Storage (Firefox) e confira a tabela de cookies, que mostra Secure, HttpOnly, SameSite e validade de cada cookie, inclusive os criados por JavaScript. Um cookie de sessão correto fica assim: Set-Cookie: sid=...; Path=/; Secure; HttpOnly; SameSite=Lax, sem Expires ou com validade curta.
Como corrigir
1. Faça um inventário dos cookies que o site define e classifique-os. Cookies de sessão e de autenticação, tokens CSRF e qualquer coisa que identifique um usuário são sensíveis. Corrija esses primeiro.
2. Defina Secure em todos os cookies de um site HTTPS; raramente há motivo para não fazer isso. Sirva o site inteiro em HTTPS e redirecione o HTTP, para que nenhuma requisição leve cookies em texto claro.
3. Defina HttpOnly em todo cookie que o JavaScript não precisa ler. Cookies de sessão nunca deveriam precisar.
4. Defina o SameSite explicitamente. Lax é o padrão certo para cookies de sessão, porque o usuário continua chegando logado a partir de links. Use Strict em cookies que protegem ações sensíveis, e SameSite=None; Secure só em cookies que precisam funcionar dentro de um iframe de terceiros ou em um fluxo entre sites.
5. Mantenha sessões curtas e faça a expiração também no servidor. O header desejado fica assim: Set-Cookie: sid=...; Path=/; Secure; HttpOnly; SameSite=Lax.
6. Configure pelo framework em vez de montar headers na mão. No ASP.NET Core, use options.Cookie.SecurePolicy = CookieSecurePolicy.Always, options.Cookie.HttpOnly = true e options.Cookie.SameSite = SameSiteMode.Lax nas opções dos cookies de autenticação e de sessão. No Express com express-session, use cookie: { secure: true, httpOnly: true, sameSite: 'lax' } e app.set('trust proxy', 1) atrás de um proxy que termina o TLS. No PHP, configure session.cookie_secure, session.cookie_httponly e session.cookie_samesite no php.ini.
7. Para cookies definidos por software que você não pode alterar, o nginx consegue acrescentar atributos com proxy_cookie_flags ~ secure httponly samesite=lax; (nginx 1.19.3 ou superior), e o Apache pode reescrevê-los com Header edit Set-Cookie.
8. Considere o prefixo __Host- no cookie de sessão. O navegador só aceita um cookie chamado __Host-sid com Secure, Path=/ e sem atributo Domain, então a proteção não pode ser removida por engano.
9. Confira de novo na tabela de cookies do navegador depois de fazer login, porque é ali que ficam os cookies que mais importam.
Verifique o seu próprio site
Comece pelo scan passivo gratuito: não precisa de conta e você recebe uma nota de A a F cobrindo cabeçalhos, TLS, cookies, DNS e outras configurações que qualquer visitante consegue observar. Ele não envia payloads de ataque, então não confirma todas as classes de falha. O scan completo, disponível com uma conta gratuita, executa as verificações ativas, incluindo a descrita nesta página.