Cómo funciona esta comprobación
Scanaris lee las cabeceras Set-Cookie que tu servidor envía al cargar la página principal y revisa los atributos de cada cookie. Comprueba tres cosas: que lleva Secure (solo se exige si la web va por HTTPS), que lleva HttpOnly y que declara SameSite. Si el nombre de la cookie sugiere sesión o autenticación (session, auth, token, sid y similares), el hallazgo es de severidad media; si no, baja. Hay un caso especial con severidad media propia: SameSite=None sin Secure, que los navegadores modernos rechazan directamente, con lo que la cookie no funciona y además delata una configuración pensada para uso entre sitios. Solo se ven las cookies que se envían en esa primera respuesta; las que se crean tras el login se comprueban mejor con el comando de más abajo o con el escaneo autenticado.
Qué significa cada atributo
- Secure: la cookie solo viaja por HTTPS. Sin él, una petición por HTTP (un enlace antiguo, una red pública) la envía en claro y cualquiera en la red la lee.
- HttpOnly: JavaScript no puede leerla. Es la diferencia entre que un XSS robe la sesión o no pueda.
- SameSite: cuándo se envía desde otro sitio.
Laxla envía en navegaciones normales pero no en peticiones iniciadas por otra web;Strictnunca desde fuera;Nonesiempre, y exigeSecure. Es la defensa principal contra CSRF.
Qué hace un atacante con una cookie de sesión sin estos atributos
Sin HttpOnly, cualquier script inyectado hace document.cookie, envía el valor a su servidor y entra con tu sesión. Sin SameSite, una página ajena puede enviar un formulario a tu web con la cookie adjunta y hacer una compra o cambiar un email en nombre del usuario. Sin Secure, basta con que el usuario abra un enlace http:// hacia tu dominio en una red compartida. En los tres casos el resultado es el mismo: alguien actúa como tu usuario.
Cómo arreglarlo según tu framework
// Express con express-session
app.use(session({
cookie: { secure: true, httpOnly: true, sameSite: 'lax' }
}));
// Next.js (Route Handler o 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: '/' });
Con Supabase en una app con servidor, la librería @supabase/ssr guarda la sesión en cookies con estos atributos; en una SPA pura la sesión va a localStorage, que es lo que mide el check de tokens en storage. En Django, Rails o Laravel los atributos se configuran en los ajustes de sesión y vienen razonables por defecto; revisa que nadie los bajó para "arreglar" un problema de desarrollo.
Comprueba que está arreglado
curl -sI https://tudominio.com | grep -i "set-cookie"
curl -sI -X POST https://tudominio.com/api/login -d "..." | grep -i "set-cookie"
Cada cookie de sesión debe mostrar Secure; HttpOnly; SameSite=Lax (o Strict). Si tu login es un formulario, mira la respuesta en las herramientas del navegador, pestaña Red, cabeceras de respuesta.
Preguntas frecuentes
Mi cookie la crea JavaScript (analítica, preferencias). No puede ser HttpOnly.
Correcto, y no pasa nada: esas cookies no dan acceso a nada. El check la marca con severidad baja por el atributo que falta; lo que importa es que las de sesión sí lo tengan.
Necesito SameSite=None porque mi app va dentro de un iframe en otro dominio.
Entonces es legítimo, siempre con Secure. Asegúrate de que el resto de defensas contra CSRF (tokens en los formularios, comprobación del Origin) están activas, porque SameSite ya no te protege.
¿Lax o Strict?
Lax para la mayoría: mantiene la sesión cuando el usuario llega desde un enlace externo. Strict si la cookie da acceso a algo muy sensible y aceptas que el usuario tenga que volver a entrar al llegar desde fuera.