Como funciona esta verificação
O Scanaris lê os cabeçalhos Set-Cookie que o teu servidor envia ao carregar a página principal e revê os atributos de cada cookie. Verifica três coisas: que leva Secure (só se exige se o site for por HTTPS), que leva HttpOnly e que declara SameSite. Se o nome do cookie sugerir sessão ou autenticação (session, auth, token, sid e semelhantes), o achado é de severidade média; se não, baixa. Há um caso especial com severidade média própria: SameSite=None sem Secure, que os navegadores modernos rejeitam diretamente, com o que o cookie não funciona e além disso denuncia uma configuração pensada para uso entre sites. Só se veem os cookies enviados nessa primeira resposta; os que se criam depois do login confirmam-se melhor com o comando mais abaixo ou com a análise autenticada.
O que significa cada atributo
- Secure: o cookie só viaja por HTTPS. Sem ele, um pedido por HTTP (um link antigo, uma rede pública) envia-o em claro e qualquer pessoa na rede o lê.
- HttpOnly: o JavaScript não o consegue ler. É a diferença entre um XSS roubar a sessão ou não conseguir.
- SameSite: quando é enviado a partir de outro site.
Laxenvia-o em navegações normais mas não em pedidos iniciados por outro site;Strictnunca a partir de fora;Nonesempre, e exigeSecure. É a defesa principal contra CSRF.
O que um atacante faz com um cookie de sessão sem estes atributos
Sem HttpOnly, qualquer script injetado faz document.cookie, envia o valor para o seu servidor e entra com a tua sessão. Sem SameSite, uma página alheia pode submeter um formulário ao teu site com o cookie anexado e fazer uma compra ou mudar um email em nome do utilizador. Sem Secure, basta que o utilizador abra um link http:// para o teu domínio numa rede partilhada. Nos três casos o resultado é o mesmo: alguém age como o teu utilizador.
Como corrigir consoante a tua framework
// Express com express-session
app.use(session({
cookie: { secure: true, httpOnly: true, sameSite: 'lax' }
}));
// Next.js (Route Handler ou Server Action)
cookies().set('session', value, { httpOnly: true, secure: true, sameSite: 'lax', path: '/' });
// Nuxt (server/api)
setCookie(event, 'session', value, { httpOnly: true, secure: true, sameSite: 'lax', path: '/' });
Com Supabase numa app com servidor, a biblioteca @supabase/ssr guarda a sessão em cookies com estes atributos; numa SPA pura a sessão vai para o localStorage, que é o que mede o check de tokens no storage. Em Django, Rails ou Laravel os atributos configuram-se nas definições de sessão e vêm razoáveis por defeito; confirma que ninguém os baixou para "resolver" um problema de desenvolvimento.
Confirma que está corrigido
curl -sI https://oteudominio.com | grep -i "set-cookie"
curl -sI -X POST https://oteudominio.com/api/login -d "..." | grep -i "set-cookie"
Cada cookie de sessão deve mostrar Secure; HttpOnly; SameSite=Lax (ou Strict). Se o teu login for um formulário, vê a resposta nas ferramentas do navegador, separador Rede, cabeçalhos de resposta.
Perguntas frequentes
O meu cookie é criado pelo JavaScript (analítica, preferências). Não pode ser HttpOnly.
Correto, e não faz mal: esses cookies não dão acesso a nada. O check marca-o com severidade baixa pelo atributo em falta; o que importa é que os de sessão o tenham.
Preciso de SameSite=None porque a minha app corre dentro de um iframe noutro domínio.
Então é legítimo, sempre com Secure. Garante que as restantes defesas contra CSRF (tokens nos formulários, verificação do Origin) estão ativas, porque o SameSite já não te protege.
Lax ou Strict?
Lax para a maioria: mantém a sessão quando o utilizador chega a partir de um link externo. Strict se o cookie dá acesso a algo muito sensível e aceitas que o utilizador tenha de voltar a entrar quando chega de fora.