Qué es CSRF y por qué necesitás protección
Cross-Site Request Forgery (CSRF) es un ataque donde un sitio malicioso engaña al navegador del usuario para ejecutar acciones no autorizadas en un sitio donde está autenticado. Por ejemplo: abrís un mail con una imagen que en realidad es un form oculto que transfiere plata de tu banco.
El ataque funciona porque los navegadores envían cookies automáticamente con cada request al dominio correspondiente. Si estás logueado en tu banco y visitás un sitio malicioso, ese sitio puede disparar requests que tu navegador autenticará con tus cookies de sesión.
Los tokens CSRF resuelven esto: son valores únicos e impredecibles que se generan por sesión y se validan en el servidor. El atacante no puede conocer el token porque está almacenado solo en tu sesión legítima, no en las cookies que el navegador envía automáticamente.
Cómo implementar tokens CSRF correctamente
La implementación básica tiene tres pasos:
1. Generación: Al crear la sesión del usuario, generá un token criptográficamente seguro (mínimo 128 bits, idealmente 256). Almacenalo en la sesión del servidor, NO en cookies.
2. Inclusión: En cada form HTML, incluí el token como campo oculto:<input type='hidden' name='csrf_token' value='tu-token-aqui'>
Para requests AJAX, envialo en headers custom: X-CSRF-Token.
3. Validación: En cada POST/PUT/DELETE, el servidor compara el token recibido con el almacenado en sesión. Si no coinciden o falta, rechazá el request con error 403.
Errores comunes: Usar el mismo token para todos los usuarios (vulnera el propósito), almacenar tokens en localStorage (accesible por XSS), o no validar en acciones de solo-lectura críticas (GET que modifican estado es anti-patrón).
Tokens por sesión vs tokens por request
Hay dos enfoques principales para la vigencia del token:
Token por sesión: Un único token válido durante toda la sesión del usuario. Pros: simple de implementar, no afecta usabilidad (refresh de página no rompe forms). Contras: si el token se filtra, es válido hasta que expire la sesión.
Token por request: Cada form/request genera un token nuevo que se invalida después de usarse. Pros: máxima seguridad, window de ataque mínimo. Contras: complejidad en apps con múltiples tabs, problemas con botón 'atrás' del navegador.
Para la mayoría de apps, token por sesión es suficiente si combinás con:
- Tokens de 256 bits (imposibles de adivinar por fuerza bruta)
- Regeneración en cambios de privilegio (login, upgrade a admin)
- Validación estricta sin excepciones
- HTTPS obligatorio (evita man-in-the-middle)
Tokens por request son para banca online, paneles de admin críticos o compliance regulatorio exigente.
Integración con frameworks y casos especiales
La mayoría de frameworks modernos tienen CSRF protection built-in, pero necesitás configurarlo correctamente:
Django: {% csrf_token %} en templates. Middleware CsrfViewMiddleware habilitado por default. Para AJAX: leer cookie csrftoken y enviar en header X-CSRFToken.
Rails: <%= csrf_meta_tags %> en layout. protect_from_forgery with: :exception en ApplicationController. Verificación automática en forms generados con helpers.
Laravel: @csrf en Blade templates. Middleware VerifyCsrfToken. Para rutas API, usá Sanctum con SPA authentication mode.
Casos especiales:
- APIs REST stateless: Si no usás sesiones, CSRF no aplica (usá OAuth2/JWT). Pero si tu API comparte cookies con el frontend, SÍ necesitás protección.
- File uploads: Incluí el token en form multipart, no como parámetro de query string.
- Webhooks: No uses CSRF tokens (el caller externo no tiene sesión). Implementá HMAC signatures o shared secrets.