Protección de Recursos Web con CORS: Una Guía Completa para Desarrolladores Seguros
Este tutorial ofrece una guía exhaustiva sobre la configuración y mitigación de riesgos asociados con el Intercambio de Recursos de Origen Cruzado (CORS). Aprenderás cómo implementar CORS de forma segura en tus aplicaciones web y cómo defenderte de posibles ataques.
La seguridad web es un pilar fundamental en el desarrollo de aplicaciones. Uno de los mecanismos esenciales para proteger tus recursos en el navegador es el Intercambio de Recursos de Origen Cruzado (CORS). Pero, ¿qué es exactamente CORS y por qué es tan importante para la seguridad de tus aplicaciones web? Vamos a desglosarlo.
📖 ¿Qué es CORS (Cross-Origin Resource Sharing)?
CORS es un mecanismo de seguridad implementado por los navegadores web que permite a un servidor indicar cualquier otro origen (dominio, esquema o puerto) aparte del suyo propio, desde el cual un navegador debe permitir la carga de recursos. Su objetivo principal es fortalecer la seguridad del navegador al prevenir que sitios web maliciosos realicen solicitudes no autorizadas a otros dominios en nombre del usuario.
Sin CORS, los navegadores imponen una política de mismo origen (Same-Origin Policy - SOP), que restringe las solicitudes HTTP iniciadas desde un script a interactuar únicamente con recursos del mismo origen del que se sirvió la página HTML. CORS relaja esta política de forma controlada.
🌐 La Política de Mismo Origen (SOP) vs. CORS
Para entender CORS, primero debemos comprender la SOP. La Política de Mismo Origen es una característica de seguridad crítica que evita que un documento o script cargado de un origen obtenga o interactúe con recursos de otro origen. Por ejemplo, un script en www.ejemplo.com no puede hacer una solicitud XMLHttpRequest a api.otrodominio.com sin el permiso explícito del servidor api.otrodominio.com.
La SOP protege a los usuarios de sitios maliciosos que intentan robar datos sensibles, como cookies de sesión, credenciales o información bancaria, de otros sitios web en los que el usuario está autenticado. CORS es la forma en que los servidores pueden optar por relajar esta restricción de seguridad para orígenes específicos.
🚀 ¿Cómo Funciona CORS? Tipos de Solicitudes
CORS introduce un conjunto de encabezados HTTP estándar que permiten a los servidores y navegadores negociar si se permite o no el acceso a un recurso desde un origen diferente. Hay dos tipos principales de solicitudes CORS:
1. Solicitudes Simples (Simple Requests)
Una solicitud se considera "simple" si cumple todas las siguientes condiciones:
- Método:
GET,HEADoPOST. - Encabezados: Solo ciertos encabezados "seguros para CORS2" se establecen automáticamente por el agente de usuario (como
Accept,Accept-Language,Content-Language,Content-Type).- Si se usa
Content-Type, su valor debe serapplication/x-www-form-urlencoded,multipart/form-dataotext/plain.
- Si se usa
Cuando un navegador detecta una solicitud simple a un origen cruzado, simplemente envía la solicitud con un encabezado Origin adicional que indica de dónde proviene la solicitud. El servidor decide si permite la solicitud basándose en la configuración de CORS y responde con el encabezado Access-Control-Allow-Origin si la solicitud es permitida.
Flujo de Solicitud Simple:
- Navegador del cliente hace una solicitud
GETaapi.ejemplo.com/datadesdewww.mi-app.com.
GET /data HTTP/1.1
Host: api.ejemplo.com
Origin: http://www.mi-app.com
- Servidor
api.ejemplo.comrecibe la solicitud. Si está configurado para permitirhttp://www.mi-app.com, responde:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.mi-app.com
Content-Type: application/json
{"mensaje": "Datos permitidos"}
- Navegador verifica el encabezado
Access-Control-Allow-Origin. Si coincide con el origen de la solicitud o es*, permite que la aplicación acceda a la respuesta.
2. Solicitudes Preflight (Preflight Requests)
Las solicitudes "no simples" o "preflighted" son aquellas que no cumplen las condiciones de una solicitud simple. Esto incluye:
- Usar métodos HTTP como
PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH. - Usar encabezados personalizados o encabezados de tipo
Content-Typecomoapplication/json.
Antes de enviar la solicitud real, el navegador envía automáticamente una solicitud OPTIONS (conocida como "preflight") al servidor para preguntar qué métodos y encabezados son seguros para usar con ese recurso. El servidor debe responder a esta solicitud preflight con información sobre sus capacidades CORS.
Flujo de Solicitud Preflight:
- Navegador del cliente quiere hacer una solicitud
POSTconContent-Type: application/jsonaapi.ejemplo.com/usersdesdewww.mi-app.com. - Navegador envía una solicitud
OPTIONS(preflight) al servidor:
OPTIONS /users HTTP/1.1
Host: api.ejemplo.com
Origin: http://www.mi-app.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
- Servidor
api.ejemplo.comrecibe la solicitudOPTIONS. Si está configurado para permitirhttp://www.mi-app.com,POSTyContent-Type, responde:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://www.mi-app.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
* `Access-Control-Allow-Origin`: Origen(es) permitidos.
* `Access-Control-Allow-Methods`: Métodos HTTP permitidos.
* `Access-Control-Allow-Headers`: Encabezados personalizados permitidos.
* `Access-Control-Max-Age`: Tiempo en segundos que la respuesta preflight puede ser cacheada.
4. Navegador recibe la respuesta preflight. Si es satisfactoria, procede a enviar la solicitud POST real:
POST /users HTTP/1.1
Host: api.ejemplo.com
Origin: http://www.mi-app.com
Content-Type: application/json
{"nombre": "Juan"}
- Servidor
api.ejemplo.comprocesa la solicitudPOSTreal y responde con los datos.
🛠️ Configuración Segura de CORS
Una configuración incorrecta de CORS puede abrir tu aplicación a vulnerabilidades significativas. La clave es ser lo más restrictivo posible.
1. Access-Control-Allow-Origin (ACAO)
Este es el encabezado más crítico. Indica qué orígenes tienen permiso para acceder a los recursos.
-
❌ Evita
Access-Control-Allow-Origin: *(Wildcard) con credenciales: Si permites el wildcard*y tambiénAccess-Control-Allow-Credentials: true, estás creando una vulnerabilidad seria. El navegador bloqueará esta combinación, pero si se desactivaAccess-Control-Allow-Credentials, el wildcard permite que cualquier sitio web realice solicitudes, lo que puede ser explotado para fugas de información o ataques CSRF si no hay otras protecciones. -
✅ Lista Blanca de Orígenes: La mejor práctica es especificar explícitamente los orígenes permitidos. Esto significa que solo tus dominios de frontend (o de otras aplicaciones que necesitan acceder a tu API) pueden interactuar con tus recursos.
# Ejemplo en Nginx
add_header 'Access-Control-Allow-Origin' 'https://mi-aplicacion-frontend.com';
add_header 'Access-Control-Allow-Origin' 'https://staging.mi-aplicacion-frontend.com';
En muchos frameworks de desarrollo, puedes configurar esto dinámicamente, verificando el encabezado `Origin` de la solicitud y respondiendo con él si está en tu lista de permitidos.
# Ejemplo en Flask (Python)
from flask import Flask, request, jsonify
from flask_cors import CORS # Necesitas pip install Flask-CORS
app = Flask(__name__)
# Lista de orígenes permitidos
allowed_origins = [
"http://localhost:3000",
"https://mi-aplicacion-frontend.com",
"https://staging.mi-aplicacion-frontend.com"
]
@app.before_request
def handle_preflight():
if request.method == 'OPTIONS':
response = jsonify({'message': 'Preflight Ok'})
origin = request.headers.get('Origin')
if origin in allowed_origins:
response.headers.add('Access-Control-Allow-Origin', origin)
response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization')
response.headers.add('Access-Control-Max-Age', '86400')
return response
@app.after_request
def add_cors_headers(response):
origin = request.headers.get('Origin')
if origin in allowed_origins:
response.headers.add('Access-Control-Allow-Origin', origin)
response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization')
return response
@app.route('/api/data')
def get_data():
return jsonify({"message": "Datos seguros!"})
if __name__ == '__main__':
app.run(debug=True)
<div class="callout tip">💡 <strong>Consejo:</strong> Para entornos de desarrollo local, `http://localhost:XXXX` suele ser un origen necesario. Recuerda eliminarlo o usar variables de entorno para controlar su presencia en producción.</div>
2. Access-Control-Allow-Methods
Especifica los métodos HTTP permitidos (GET, POST, PUT, DELETE, etc.). Sé explícito y permite solo los métodos necesarios para cada endpoint.
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
3. Access-Control-Allow-Headers
Indica qué encabezados personalizados pueden ser usados en las solicitudes reales. Si tu frontend envía un encabezado Authorization, debes permitirlo aquí.
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
4. Access-Control-Allow-Credentials
Si tu aplicación necesita enviar cookies, encabezados de autorización HTTP o certificados TLS con las solicitudes de origen cruzado, debes establecer este encabezado a true y el cliente debe usar credentials: 'include' en su Fetch API o withCredentials = true en XMLHttpRequest.
5. Access-Control-Max-Age
Define por cuánto tiempo (en segundos) el navegador puede cachear la respuesta preflight. Esto reduce el número de solicitudes OPTIONS y mejora el rendimiento. Un valor común es 86400 segundos (24 horas).
add_header 'Access-Control-Max-Age' '86400';
🛡️ Vulnerabilidades y Mala Configuración de CORS
Una implementación incorrecta de CORS puede llevar a graves vulnerabilidades de seguridad. Aquí algunas de las más comunes:
1. Wildcard * con Dominios Confiables Falsos
Algunas implementaciones intentan ser inteligentes y devuelven el valor del encabezado Origin en Access-Control-Allow-Origin si Origin termina en un dominio "confiable".
Por ejemplo, si un servidor permite *.ejemplo.com:
# Solicitud de un atacante
GET /data HTTP/1.1
Host: api.servidor.com
Origin: http://malicioso.ejemplo.com.evil.com
# Respuesta del servidor vulnerable
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://malicioso.ejemplo.com.evil.com # ¡Vulnerable!
Un atacante podría registrar un dominio como ejemplo.com.evil.com y engañar a la lógica de validación, obteniendo acceso a recursos sensibles.
2. Orígenes Nulos (null Origin)
El encabezado Origin: null puede ser enviado por el navegador en ciertas situaciones, como redirecciones (data: o file: URLs) o el uso de iframes sandbox. Si tu configuración CORS permite el origen null, un atacante podría crear un archivo HTML local o un iframe sandbox que interactúe con tu API.
# Evita esto si no es estrictamente necesario
add_header 'Access-Control-Allow-Origin' 'null';
3. Reflexión del Origin sin Validación
Simplemente reflejar el encabezado Origin de la solicitud en la respuesta Access-Control-Allow-Origin sin ninguna validación es una de las vulnerabilidades más críticas.
// Ejemplo PHP (Vulnerable)
if (isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
}
Esto permite a cualquier dominio realizar solicitudes de origen cruzado, eludiendo completamente la SOP y permitiendo ataques como el robo de tokens CSRF, información sensible o ejecución de acciones maliciosas.
4. Mala Configuración del Access-Control-Allow-Headers
Permitir encabezados personalizados innecesarios podría, en casos raros y combinados con otras vulnerabilidades, abrir una puerta a ataques de inyección. Sé restrictivo también con los encabezados permitidos.
5. Servidores Frontend y Backend en el Mismo Origen
Si tu frontend y backend están en el mismo dominio (ej. mi-app.com/frontend y mi-app.com/api), CORS no es necesario porque están bajo la misma Política de Mismo Origen. La configuración de CORS en este escenario podría ser superflua, pero si se planea desacoplar los servicios en el futuro, es bueno considerarlo.
🕵️ Cómo Auditar y Probar la Configuración CORS
Es crucial auditar tu configuración CORS para asegurar que no introduces vulnerabilidades.
1. Herramientas del Navegador (DevTools)
La forma más sencilla es usar las herramientas de desarrollo de tu navegador (Chrome DevTools, Firefox Developer Tools). Abre la pestaña "Network" y observa las solicitudes HTTP. Los errores CORS se mostrarán en la consola y las respuestas preflight serán visibles.
F12 (o Ctrl + Shift + I en Windows/Linux, Cmd + Option + I en Mac) para abrir las DevTools.
2. curl o Postman/Insomnia
Puedes simular solicitudes de origen cruzado usando curl o herramientas como Postman. Incluye el encabezado Origin para ver cómo responde el servidor.
# Simular una solicitud simple
curl -v -H "Origin: https://malicious-site.com" https://your-api.com/data
# Simular una solicitud preflight
curl -v -X OPTIONS -H "Origin: https://malicious-site.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
https://your-api.com/users
Busca los encabezados Access-Control-Allow-Origin, Access-Control-Allow-Methods, etc., en la respuesta.
3. Escáneres de Vulnerabilidades
Herramientas como Burp Suite o ZAP (OWASP Zed Attack Proxy) tienen funcionalidades para detectar configuraciones CORS inseguras.
✅ Mejores Prácticas para una Implementación Segura de CORS
Sigue estas directrices para implementar CORS de manera robusta y segura:
- Principio de Mínimo Privilegio: Permite solo los orígenes, métodos y encabezados HTTP que sean absolutamente necesarios.
- Lista Blanca Estricta: Mantén una lista estricta y explícita de dominios permitidos para
Access-Control-Allow-Origin. Evita el wildcard*en producción siAccess-Control-Allow-Credentials: truees requerido. - Validación del
Origin: Si necesitas unAccess-Control-Allow-Origindinámico, valida rigurosamente el encabezadoOriginde la solicitud contra una lista blanca predefinida o expresiones regulares robustas. NO lo reflejes directamente. Access-Control-Max-Age: Úsalo para mejorar el rendimiento, pero ten en cuenta que un valor muy alto podría retrasar la aplicación de cambios en la política CORS.- Manejo de Credenciales: Si necesitas enviar credenciales (cookies, encabezados de autorización), asegúrate de establecer
Access-Control-Allow-Credentials: truey queAccess-Control-Allow-Originno sea*. - Pruebas Exhaustivas: Realiza pruebas de seguridad regulares (incluyendo pen testing) para identificar cualquier mala configuración de CORS.
- Documentación: Documenta claramente tu política CORS y por qué se han tomado ciertas decisiones.
📊 Resumen de Encabezados CORS Clave
| Encabezado HTTP | Propósito | Recomendación de Seguridad |
|---|---|---|
| --- | --- | --- |
Origin (Request) | Indica el origen de la solicitud de origen cruzado. | N/A (automático por el navegador). |
Access-Control-Allow-Origin | Especifica qué orígenes pueden acceder al recurso. | Lista blanca estricta. Evitar * con credenciales. No reflejar sin validación. |
| --- | --- | --- |
Access-Control-Allow-Methods | Enumera los métodos HTTP permitidos (GET, POST, etc.). | Mínimo privilegio. Permitir solo los métodos necesarios. |
Access-Control-Allow-Headers | Especifica qué encabezados personalizados están permitidos. | Mínimo privilegio. Permitir solo los encabezados requeridos (ej. Authorization). |
| --- | --- | --- |
Access-Control-Allow-Credentials | Indica si el navegador debe enviar cookies o encabezados de autorización. | Usar true solo si es necesario y con ACAO explícito (no *). |
Access-Control-Max-Age | Duración en segundos para cachear la respuesta preflight. | Usar un valor razonable (ej. 86400s). |
Conclusión
CORS es una herramienta indispensable en el arsenal de seguridad web, diseñada para proteger a los usuarios de ataques de origen cruzado al imponer y relajar de forma controlada la Política de Mismo Origen. Comprender cómo funciona y, más importante aún, cómo configurarlo de forma segura es vital para cualquier desarrollador que construya aplicaciones web modernas.
Al seguir las mejores prácticas de una lista blanca estricta de orígenes, métodos y encabezados, y al evitar configuraciones permisivas o ingenuas, puedes asegurarte de que tus aplicaciones están bien protegidas contra vulnerabilidades de origen cruzado.
Tutoriales relacionados
- Protección contra Clickjacking: Defiende a tus Usuarios de Interacciones Maliciosasintermediate10 min
- Asegurando el Cifrado de Transporte: Desplegando TLS/SSL Robusto para tu Webintermediate15 min
- Escudriñando tu Código: Descubriendo Vulnerabilidades con Análisis Estático (SAST)intermediate20 min
- Asegurando tu API REST: Implementación de Autenticación y Autorización Robustasintermediate20 min
- Asegurando tus Aplicaciones Web: Una Guía Completa de Hardeningintermediate12 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!