FundamentosLección 08
Mismo origen, CSRF y CORS
La regla que protege a los navegadores, el ataque que abusa de las cookies automáticas y el mecanismo que relaja la regla de forma controlada. Tres piezas que solo se entienden juntas.
Al terminar sabrás
- Saber qué es un origen y qué permite y prohíbe la política de mismo origen.
- Explicar un ataque CSRF y por qué funcionan el token CSRF y
SameSite. - Entender qué hace CORS, qué es un preflight y por qué CORS no protege tu API.
- Decidir cuándo una API puede prescindir de la protección CSRF.
Tu navegador tiene abiertas a la vez la web de tu banco, tu correo y una página cualquiera. Todas ejecutan JavaScript en el mismo programa. Lo que impide que la página cualquiera lea tu correo es una regla del navegador: la política de mismo origen. CSRF y CORS son, respectivamente, un ataque que la esquiva y un mecanismo para relajarla.
Qué es un origen
Sección titulada «Qué es un origen»https://esquemaapp.example.comhost:443puerto/api/notes?id=5ruta (no forma parte del origen)
origen
URL comparada con https://app.example.com | ¿Mismo origen? | Por qué |
|---|---|---|
https://app.example.com/otra/ruta | Sí | Solo cambia la ruta |
http://app.example.com | No | Cambia el esquema |
https://api.example.com | No | Cambia el host (un subdominio es otro origen) |
https://app.example.com:8443 | No | Cambia el puerto |
Mismo sitio (same-site) es un concepto más amplio: app.example.com y api.example.com son orígenes distintos pero el mismo sitio (example.com). SameSite en las cookies se basa en el sitio, no en el origen.
La política de mismo origen
Sección titulada «La política de mismo origen»Una página de evil.example puede:
- Enviar un formulario
POSTabank.example(y el navegador adjuntará las cookies debank.examplesegún suSameSite). - Cargar imágenes, scripts y hojas de estilo de cualquier origen.
- Hacer
fetcha cualquier origen… pero no leer la respuesta salvo que ese origen lo permita con CORS.
CSRF: el ataque
Sección titulada «CSRF: el ataque»Cross-Site Request Forgery hace que el navegador de la víctima envíe una petición que la víctima no quería, aprovechando que el navegador adjunta las cookies automáticamente.
<form action="https://bank.example/transfer" method="POST" id="f"> <input type="hidden" name="to" value="666"> <input type="hidden" name="amount" value="1000"></form><script>document.getElementById('f').submit();</script>Las defensas
Sección titulada «Las defensas»- Token CSRF (sincronizador): el servidor genera un valor secreto aleatorio ligado a la sesión, lo incluye en sus formularios o lo entrega al JavaScript de su propio origen, y exige que cada petición que cambia estado lo devuelva. La página atacante no puede leerlo (política de mismo origen), así que no puede incluirlo.
- Cookies
SameSite=LaxoStrict: el navegador no adjunta la cookie a peticiones POST iniciadas desde otro sitio. Es una defensa excelente, pero no cubre a navegadores antiguos ni ataques desde otro subdominio del mismo sitio. Úsala además del token, no en su lugar. - No cambiar estado con GET: GET, HEAD y OPTIONS deben ser seguros (sin efectos).
SameSite=Laxdeja pasar los GET de navegación, y casi ningún framework protege GET con token. - Comprobar
Origin/Sec-Fetch-Sitecomo capa extra: el navegador indica de dónde viene la petición.
CORS: relajar la regla de forma controlada
Sección titulada «CORS: relajar la regla de forma controlada»Tu front en https://app.example.com necesita leer respuestas de tu API en https://api.example.com. Son orígenes distintos, así que el navegador bloquearía la lectura. CORS (Cross-Origin Resource Sharing) es la forma en que la API le dice al navegador: “este origen sí puede leerme”.
Para peticiones “no simples” (con Authorization, con Content-Type: application/json, o con métodos como PUT y DELETE), el navegador pregunta primero con un preflight:
OPTIONS /notes/42 HTTP/1.1Origin: https://app.example.comAccess-Control-Request-Method: DELETEAccess-Control-Request-Headers: authorization
HTTP/1.1 204 No ContentAccess-Control-Allow-Origin: https://app.example.comAccess-Control-Allow-Methods: GET, POST, PUT, DELETEAccess-Control-Allow-Headers: authorization, content-typeAccess-Control-Allow-Credentials: true¿Necesita mi API protección CSRF?
Sección titulada «¿Necesita mi API protección CSRF?»| Caso | CSRF |
|---|---|
API stateless autenticada con Authorization: Bearer | No aplica: se puede desactivar |
| API usada solo por otros servicios (nunca un navegador) | No aplica |
| Aplicación web con sesión y formularios | Activado |
| SPA que usa una cookie de sesión (patrón BFF) | Activado (token en cabecera) |
| JWT guardado en una cookie | Activado: la cookie vuelve a ir sola |
| Webhook externo dentro de una app con sesión | Exceptuar solo esa ruta y validar la firma del webhook |
Mini lab · 12 min
Ataca y defiende
Objetivo: Razonar sobre CSRF y CORS en escenarios concretos antes de configurarlos en Spring.
Tu banco usa sesión con cookie SESSION (HttpOnly; Secure, sin atributo SameSite) y tiene POST /transfer. Su front vive en https://bank.example y llama a la API https://api.bank.example con fetch(..., { credentials: 'include' }).
- ¿Funcionaría el formulario oculto del atacante de arriba? ¿Qué dos cambios lo bloquearían?
- La API responde a todo preflight con
Access-Control-Allow-Origin: <el Origin recibido>yAllow-Credentials: true. ¿Qué puede hacer ahoraevil.exampleque antes no podía? - Un compañero dice: “desactivemos CSRF, total ya tenemos CORS configurado”. ¿Qué le respondes?
Pista
Recuerda la asimetría: la política de mismo origen bloquea leer, no enviar.
Mostrar respuestaOcultar respuesta
- Sí funcionaría: un formulario HTML es una petición “simple” (no hay preflight) y, sin
SameSite, el navegador adjunta la cookie. Lo bloquean: (a) exigir un token CSRF enPOST /transfer, y (b) marcar la cookie conSameSite=Lax(oStrict). Ambas juntas son la defensa recomendada. - Ahora
evil.examplepuede hacerfetchconcredentials: 'include'y leer la respuesta: el saldo, los movimientos, y también el token CSRF si hay un endpoint que lo devuelve, con lo que podría saltarse la protección CSRF. Hay que limitarAllow-Originahttps://bank.example. - Que CORS no protege contra CSRF: un formulario
POSTsimple no lanza preflight, el navegador lo envía igualmente con la cookie, y el servidor ejecuta la acción aunque el atacante no pueda leer la respuesta. Mientras la autenticación viaje en una cookie, CSRF debe seguir activo.