Las cookies son el mecanismo principal de gestión de sesiones en la web. Una cookie de sesión mal configurada es una puerta abierta: permite robo de sesión (XSS), suplantación de identidad (CSRF) y violaciones de privacidad (RGPD). La buena noticia: configurar cookies de forma segura requiere apenas unas líneas de código.
Anatomía de una cookie segura
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600; Domain=tusitio.es
Cada atributo tiene una función de seguridad específica:
Los flags de seguridad
HttpOnly
Set-Cookie: session=abc123; HttpOnly
Qué hace: impide que JavaScript acceda a la cookie a través de document.cookie.
Por qué importa: si un atacante logra ejecutar un XSS en tu sitio, sin HttpOnly puede robar la cookie de sesión con document.cookie. Con HttpOnly, la cookie es inaccesible desde JavaScript — solo se envía automáticamente en las peticiones HTTP.
Cuándo usarlo: SIEMPRE en cookies de sesión y tokens de autenticación. NUNCA en cookies que JavaScript necesita leer (preferencias de tema, idioma).
Secure
Set-Cookie: session=abc123; Secure
Qué hace: la cookie solo se envía en conexiones HTTPS.
Por qué importa: sin Secure, la cookie se envía también por HTTP (sin cifrar), permitiendo su interceptación en redes no seguras (cafeterías, aeropuertos).
Cuándo usarlo: SIEMPRE en producción. En desarrollo local (localhost), los navegadores hacen una excepción.
SameSite
Set-Cookie: session=abc123; SameSite=Lax
Qué hace: controla cuándo se envía la cookie en peticiones cross-site.
| Valor | Comportamiento | Protección CSRF |
|-------|---------------|-----------------|
| Strict | Solo se envía en peticiones del mismo sitio | Máxima (pero puede romper flujos de navegación) |
| Lax | Se envía en navegación directa (links), no en formularios/AJAX cross-site | Buena (recomendado) |
| None | Se envía siempre (requiere Secure) | Ninguna (solo si es necesario) |
Recomendación: Lax para la mayoría de los casos. Strict si no necesitas que la cookie funcione al llegar desde otro sitio. None solo para cookies de terceros (que están siendo eliminadas por los navegadores).
Path
Set-Cookie: session=abc123; Path=/
Qué hace: limita la cookie a un path específico del sitio.
Recomendación: Path=/ para cookies de sesión. Paths más restrictivos para cookies específicas de secciones.
Domain
Set-Cookie: session=abc123; Domain=tusitio.es
Qué hace: define en qué dominios se envía la cookie. Si especificas Domain=tusitio.es, la cookie se envía también a subdominios (api.tusitio.es, admin.tusitio.es).
Recomendación: omitir Domain si la cookie solo debe funcionar en el dominio exacto (más restrictivo).
Max-Age / Expires
Set-Cookie: session=abc123; Max-Age=3600
Qué hace: define cuándo expira la cookie.
Max-Age=3600: expira en 1 hora- Sin Max-Age ni Expires: cookie de sesión (se borra al cerrar el navegador)
Recomendación: sesiones activas de 30 min a 24h máximo. Remember-me: 30 días máximo con renovación.
Prefijos de cookies
Los navegadores modernos soportan prefijos especiales:
__Secure-
Set-Cookie: __Secure-session=abc123; Secure; Path=/
El navegador rechaza esta cookie si no cumple el flag Secure. Previene que un atacante en HTTP sobrescriba la cookie.
__Host-
Set-Cookie: __Host-session=abc123; Secure; Path=/; SameSite=Lax
Más restrictivo: requiere Secure, Path=/, y NO puede tener Domain. La cookie solo funciona en el dominio exacto. Es la opción más segura para cookies de sesión.
Configuración por framework
Express.js
app.use(session({
name: '__Host-session',
secret: process.env.SESSION_SECRET,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 1800000, // 30 minutos
path: '/',
},
resave: false,
saveUninitialized: false,
}));
Next.js (Server Actions / API Routes)
import { cookies } from 'next/headers';
const cookieStore = await cookies();
cookieStore.set('session', tokenValue, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 1800, // 30 minutos en segundos
path: '/',
});
PHP
session_set_cookie_params([
'lifetime' => 1800,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
Errores comunes
No usar HttpOnly en cookies de sesión. Es la protección más básica contra robo de sesión por XSS. No hay excusa para omitirlo.
SameSite=None sin Secure. Los navegadores rechazan cookies con SameSite=None que no tengan Secure.
Cookies de sesión sin expiración. Una cookie sin Max-Age es cookie de sesión del navegador (se borra al cerrar), pero si el usuario nunca cierra el navegador, la sesión dura para siempre.
Almacenar datos sensibles en la cookie. La cookie debe contener solo el ID de sesión, nunca datos del usuario (email, rol, permisos).
No regenerar la sesión tras login. Después de autenticar al usuario, genera un nuevo token de sesión para prevenir session fixation.
Checklist cookies seguras
- [ ]
HttpOnlyen todas las cookies de sesión - [ ]
Secureen producción - [ ]
SameSite=Lax(oStrictsi posible) - [ ]
Max-Agerazonable (≤24h para sesiones activas) - [ ] Prefijo
__Host-para cookies de sesión - [ ] Regeneración de sesión tras login
- [ ] Solo el session ID en la cookie (no datos sensibles)
- [ ] Invalidación server-side al logout
Cómo WarDek verifica tus cookies
WarDek analiza automáticamente las cookies de tu sitio web:
- Detección de flags: verifica HttpOnly, Secure, SameSite en cada cookie
- Clasificación: técnica, analítica o marketing
- Duración: detecta cookies con duración excesiva
- Conformidad RGPD: verifica consentimiento y banner
Escanea tu sitio web gratis con WarDek — OWASP, NIS2, RGPD, AI Act en un solo escaneo.