SeguridadCSRFAutenticaciónSameSiteRevisión de código

SameSite=Lax no te protege del login-CSRF

Publicado el 2026-08-31 · Xiliux

Construimos un servidor de identidad —Bachué— que autentica con contraseña, segundo factor y sesión por cookie. Las cookies llevan SameSite=Lax, y dábamos por sentado lo que "todo el mundo sabe": que SameSite=Lax protege del CSRF.

No nos fiamos de nuestra propia revisión. El autor de un código es el peor situado para auditarlo: lo que pensó ya lo siente verificado. Así que le pedimos a tres modelos distintos que lo atacaran, cada uno por su cuenta, con un solo encargo: encuentra el defecto más grave; piensa como quien quiere entrar.

Una falsa alarma, y luego la de verdad

El primer modelo gritó "¡CSRF!" con la mecánica equivocada: creía que SameSite=Lax deja pasar un POST cross-site. No lo deja —Lax no envía la cookie en un POST de otro sitio—. Un segundo modelo lo refutó. Hasta ahí, ruido.

El tercero encontró el real, y es sutil: el login-CSRF, que SameSite no cubre.

Por qué Lax protege el logout pero no el login

SameSite=Lax protege los endpoints que necesitan la cookie de la víctima: cerrar sesión, cambiar ajustes. Un POST desde el sitio del atacante llega sin esa cookie, el servidor no ve sesión, y no pasa nada.

Pero /entrar y /registrarse son distintos: no necesitan la cookie de la víctima. El atacante pone sus propias credenciales en el formulario, y lo único que hace el servidor es fijar una cookie nueva en la respuesta. Y un Set-Cookie de primera parte se honra siempre, venga la petición de donde venga. Resultado: la víctima queda navegando autenticada como el atacante, sin saberlo —y todo lo que teclee después vive en la cuenta del atacante—. SameSite no tiene ningún efecto sobre esto.

Afirmar no es demostrar

"SameSite cubre el CSRF" era una afirmación, y como toda frontera de seguridad, parecía aguantar hasta que alguien la cruzó. La demostración —tres adversarios independientes— costó minutos y encontró lo que una sola mirada, incluida la nuestra, habría dado por bueno.

El arreglo es una línea de disciplina: todo POST que cambie estado —también los que aún no exigen sesión— comprueba que la petición es de primera parte (Sec-Fetch-Site) antes de procesarla. Como regla global, no como parche por endpoint que el próximo olvide.

La lección vale más que el bug: no basta con revisar tu propio código de seguridad. Ponle adversarios independientes a demostrar que la frontera aguanta. Si solo la afirmas, un día no aguanta —callada, en el punto donde nadie volvió a mirar—.

Preguntas frecuentes

¿SameSite=Lax no protege del CSRF?

Protege los endpoints que NECESITAN la cookie de la víctima (logout, ajustes): un POST cross-site llega sin la cookie y falla. Pero no protege el login-CSRF, porque /entrar no necesita la cookie de la víctima —el atacante usa sus propias credenciales y el servidor fija una cookie nueva—.

¿Cómo se defiende el login-CSRF?

Con un chequeo de primera parte en TODO POST que cambie estado, incluidos los que no exigen sesión: verificar Sec-Fetch-Site (o un token CSRF) antes de procesar. Como regla global, no endpoint por endpoint.

¿Por qué revisar con varios modelos?

El autor es el peor situado para auditar su código. Modelos independientes y distintos entre sí recalibran las falsas alarmas de uno y encuentran lo que otro no ve; aquí uno erró la mecánica, otro la refutó y un tercero halló el bug real.

← Ver más artículosCotizar un proyecto