CORS

Concepto General

CORS (Cross-Origin Resource Sharing) es un mecanismo de seguridad que controla cómo los recursos de un sitio web pueden ser solicitados desde otro dominio distinto al de origen. Se implementa mediante cabeceras HTTP que indican qué dominios, métodos y cabeceras están permitidos al realizar peticiones entre orígenes.

Su objetivo principal es evitar accesos no autorizados o fugas de datos al consumir APIs desde navegadores, manteniendo la integridad del Backend y la protección de los usuarios en el contexto de ciberseguridad.

Orígenes

El origen se define como la combinación de protocolo, dominio y puerto.
Dos URLs pertenecen al mismo origen solo si los tres componentes coinciden exactamente.

Ejemplo:

  • https://api.misitio.com:443 y https://api.misitio.com → mismo origen
  • https://app.misitio.com y https://api.misitio.com → distinto origen
  • http://localhost:3000 y https://localhost:3000 → distinto origen

Cuando una aplicación cliente intenta acceder a recursos desde un origen diferente, el navegador evalúa las cabeceras CORS del servidor antes de permitir o bloquear la solicitud.

Cabeceras HTTP

Las cabeceras CORS determinan las reglas de acceso:

  • Access-Control-Allow-Origin: especifica qué origen puede acceder (* para todos, o una URL específica).
  • Access-Control-Allow-Methods: lista de métodos HTTP permitidos (GET, POST, PUT, DELETE, etc.).
  • Access-Control-Allow-Headers: define las cabeceras personalizadas aceptadas por el servidor.
  • Access-Control-Allow-Credentials: indica si las cookies o credenciales están permitidas.
  • Access-Control-Max-Age: tiempo en segundos que el navegador puede almacenar la política CORS en caché.

Ejemplo de Respuesta del Servidor

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

`

Librería cors en Backend

En entornos como Node.js o Express.js, la librería cors simplifica la configuración mediante middleware.

Instalación

npm install cors

Uso Básico

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());

Este ejemplo permite el acceso desde cualquier origen (*), lo cual puede ser útil para desarrollo pero no se recomienda en producción.

Configuración Personalizada

const corsOptions = {
	origin: 'https://frontend.com',
	methods: ['GET', 'POST'],
	allowedHeaders: ['Content-Type', 'Authorization'],
	credentials: true
};

app.use(cors(corsOptions));

Whitelist de Orígenes

Para mayor seguridad, puede definirse una lista blanca (whitelist) de dominios permitidos.

const whitelist = ['https://frontend.com', 'https://admin.frontend.com'];

const corsOptions = {
	origin: (origin, callback) => {
		if (!origin || whitelist.includes(origin)) {
			callback(null, true);
		} else {
			callback(new Error('No autorizado por CORS'));
		}
	}
};

app.use(cors(corsOptions));

Esta configuración evalúa el origen de cada solicitud y solo permite los que estén en la lista.

Casos Prácticos

  • Entorno local: permitir http://localhost y http://127.0.0.1 para desarrollo.
  • APIs públicas: configurar Access-Control-Allow-Origin: * solo si no se manejan credenciales.
  • Producción: utilizar whitelist y credenciales solo cuando sea necesario.

Buenas Prácticas de Seguridad

  • No usar * en Access-Control-Allow-Origin si se manejan cookies o tokens.
  • Validar los orígenes en el servidor, no confiar en el cliente.
  • Registrar los intentos fallidos de acceso para auditoría.
  • Comprobar configuraciones CORS en entornos de staging antes del despliegue.

Errores Comunes

  • Bloqueo de navegador: ocurre cuando el servidor no devuelve las cabeceras adecuadas.
  • Preflight request fallida: error en la respuesta al método OPTIONS.
  • Cabeceras ausentes: olvidar incluir Access-Control-Allow-Headers cuando se usan cabeceras personalizadas.

Diagnóstico y Depuración

  • Usar herramientas de red del navegador (pestaña Network) para ver las cabeceras.
  • Revisar el log del servidor para detectar rechazos de origen.
  • Probar con curl o Postman para verificar manualmente las respuestas.

Ejemplo de prueba con curl

curl -I -X OPTIONS https://api.misitio.com -H "Origin: https://frontend.com"

Esto permite inspeccionar las cabeceras de respuesta CORS sin depender del navegador.

CORS Avanzado y Casos de Implementación

Profundización Técnica

CORS no solo gestiona peticiones simples; también regula las llamadas preflight, las cuales se ejecutan automáticamente antes de una solicitud principal cuando se cumplen ciertas condiciones (por ejemplo, métodos distintos de GET o POST, o cabeceras personalizadas).

Solicitudes Preflight

El navegador envía una solicitud OPTIONS al servidor para verificar si el origen, método y cabeceras están permitidos.

Ejemplo de Flujo Preflight

  1. Cliente envía:
   OPTIONS /api/recurso HTTP/1.1
   Origin: https://frontend.com
   Access-Control-Request-Method: POST
   Access-Control-Request-Headers: Content-Type

`

  1. Servidor responde:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 3600

Si la respuesta cumple las condiciones CORS, el navegador procede con la solicitud real (POST).

Integración con APIs REST y GraphQL

En arquitecturas modernas, especialmente con microservicios o APIs distribuidas, la correcta configuración CORS es esencial para el consumo desde clientes SPA o móviles.

Ejemplo REST

  • Backend en https://api.misitio.com
  • Frontend en https://app.misitio.com

El backend debe responder con:

Access-Control-Allow-Origin: https://app.misitio.com

Ejemplo GraphQL

app.use(
	'/graphql',
	cors({ origin: 'https://frontend.com', credentials: true }),
	express.json(),
	expressMiddleware(schema)
);

Esto permite peticiones autenticadas de un dominio controlado sin exponer credenciales a otros orígenes.

CORS en Frontend

Aunque el control principal recae en el servidor, el cliente puede adaptarse a los requisitos del servidor:

  • Definir el mode en fetch:
  fetch('https://api.misitio.com/datos', { mode: 'cors' });
  • Incluir credenciales si el servidor lo permite:
  fetch('https://api.misitio.com/secure', {
  	mode: 'cors',
  	credentials: 'include'
  });

Estas configuraciones dependen del Access-Control-Allow-Credentials y el dominio autorizado.

CORS y Autenticación

Cuando se usan tokens JWT o sesiones, la configuración CORS se vuelve crítica:

  • JWT: los tokens se envían en la cabecera Authorization. El servidor debe incluir esta cabecera en Access-Control-Allow-Headers.
  • Cookies de sesión: requieren credentials: true y un origen explícito (no *).

Ejemplo de backend seguro:

app.use(
	cors({
		origin: 'https://frontend.com',
		credentials: true,
		allowedHeaders: ['Authorization', 'Content-Type']
	})
);

CORS en Nginx y Apache

CORS también puede configurarse a nivel de proxy o servidor web.

Nginx

location /api/ {
	add_header 'Access-Control-Allow-Origin' 'https://frontend.com';
	add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
	add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
	if ($request_method = OPTIONS) {
		return 204;
	}
}

Apache

<Directory "/var/www/api">
	Header set Access-Control-Allow-Origin "https://frontend.com"
	Header set Access-Control-Allow-Methods "GET,POST,OPTIONS"
	Header set Access-Control-Allow-Headers "Authorization,Content-Type"
</Directory>

CORS y Reverse Proxy

En arquitecturas donde un reverse proxy (como Nginx o Traefik) se encarga de enrutar peticiones, CORS puede manejarse directamente en el proxy, aliviando la carga del Backend. Esto facilita aplicar políticas centralizadas para múltiples microservicios.

Testing y Validación

Herramientas útiles:

  • Postman y Insomnia para probar cabeceras manualmente.
  • curl para solicitudes controladas.
  • Extensiones de navegador como Allow CORS: Access-Control-Allow-Origin (solo para pruebas, nunca en producción).

Automatización en testing:

import request from 'supertest';
import app from '../app';

test('Permite solicitudes desde origen válido', async () => {
	const res = await request(app)
		.get('/api/data')
		.set('Origin', 'https://frontend.com');
	expect(res.headers['access-control-allow-origin']).toBe('https://frontend.com');
});

Estrategias Avanzadas

  • Dynamic Origin Handling: permitir orígenes basados en configuración o entorno.
  • Rate Limiting por Origen: limitar tráfico según el dominio para mitigar abusos.
  • Registro de Solicitudes CORS: almacenar intentos de acceso no autorizados para análisis de seguridad.

Conclusión Técnica

CORS es una capa crítica en la arquitectura web moderna que conecta seguridad, accesibilidad y diseño de APIs. Su mala configuración puede exponer vulnerabilidades o impedir la comunicación legítima entre servicios. La clave está en aplicar políticas precisas, no genéricas, integradas dentro de un contexto seguro de Backend, ciberseguridad y despliegue.

CORS: Casos Avanzados, Configuraciones Complejas y Escenarios Especiales

CORS en Microservicios y Arquitecturas Distribuidas

En entornos basados en microservicios, el control de CORS puede descentralizarse o centralizarse según la topología:

  • Centralizado (en gateway o API Gateway): el control CORS se gestiona en un punto de entrada común, simplificando la configuración.
  • Descentralizado (en cada microservicio): cada servicio define su propia política, útil si hay diferentes dominios o niveles de acceso.

Ejemplo con Express.js y API Gateway:

gateway.use('/api/users', cors({ origin: 'https://app.frontend.com' }), usersService);
gateway.use('/api/payments', cors({ origin: 'https://admin.frontend.com' }), paymentsService);

`

CORS en WebSockets

Aunque CORS no se aplica directamente a WebSockets (ws:// o wss://), el concepto de origen confiable sigue siendo relevante. Los navegadores solo permiten conexiones WebSocket al mismo origen o a dominios que implementen medidas equivalentes de validación en el handshake.

Configuración típica:

const wsServer = new WebSocketServer({
	verifyClient: (info, done) => {
		const origin = info.origin;
		const allowed = ['https://frontend.com', 'https://admin.frontend.com'];
		if (allowed.includes(origin)) done(true);
		else done(false, 403, 'Origin not allowed');
	}
});

CORS en Aplicaciones Móviles y pwa

Las aplicaciones móviles híbridas (por ejemplo, con capacitor o Cordova) no siempre ejecutan restricciones CORS, ya que operan fuera del contexto del navegador. Sin embargo, en pwa o entornos que usan Service Workers, las políticas CORS siguen aplicándose para proteger las respuestas cacheadas o interceptadas.

Ejemplo de manejo con fetch y Service Worker:

self.addEventListener('fetch', event => {
	event.respondWith(
		fetch(event.request, { mode: 'cors' })
			.then(response => response)
			.catch(() => caches.match(event.request))
	);
});

CORS y CDN

Cuando los recursos (imágenes, fuentes, scripts) se sirven desde un CDN, es esencial que las cabeceras CORS estén configuradas correctamente para permitir su uso desde otros dominios.

Ejemplo en AWS S3:

<CORSConfiguration>
	<CORSRule>
		<AllowedOrigin>https://app.frontend.com</AllowedOrigin>
		<AllowedMethod>GET</AllowedMethod>
		<AllowedHeader>*</AllowedHeader>
		<ExposeHeader>Content-Length</ExposeHeader>
	</CORSRule>
</CORSConfiguration>

Esto habilita el uso de recursos estáticos en un frontend alojado en otro dominio.

CORS y HTTP/2 o HTTP/3

Con protocolos modernos, las cabeceras CORS siguen funcionando igual, pero su interpretación puede verse afectada por:

  • Multiplexación de peticiones.
  • Cabeceras pseudo (:method, :path, etc.).
  • Compresión y priorización de streams.

En estos contextos, el control CORS sigue siendo una capa lógica sobre HTTP, no una limitación técnica del protocolo.

CORS en Cloudflare, AWS API Gateway y Azure API Management

Cloudflare

Permite definir reglas de cabeceras personalizadas en su panel o mediante Transform Rules:

Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: GET, POST, OPTIONS

AWS API Gateway

En APIs gestionadas, las políticas CORS se definen por recurso:

{
	"Access-Control-Allow-Origin": "'https://frontend.com'",
	"Access-Control-Allow-Methods": "'GET,POST,OPTIONS'"
}

Azure API Management

Usa Inbound Policies XML:

<inbound>
	<base />
	<cors>
		<allowed-origins>
			<origin>https://frontend.com</origin>
		</allowed-origins>
		<allowed-methods>
			<method>GET</method>
			<method>POST</method>
		</allowed-methods>
	</cors>
</inbound>

CORS y SSE (Server-Sent Events)

Al usar event streams, el navegador sigue respetando CORS. El servidor debe enviar cabeceras adecuadas antes de abrir el stream.

Ejemplo:

res.writeHead(200, {
	'Content-Type': 'text/event-stream',
	'Access-Control-Allow-Origin': 'https://frontend.com',
	'Cache-Control': 'no-cache',
	Connection: 'keep-alive'
});

CORS en Testing y Automatización QA

Las pruebas automáticas deben incluir escenarios CORS para evitar bloqueos en producción:

  • Simular orígenes distintos con herramientas como supertest o playwright.
  • Incluir pruebas de preflight y validación de credenciales.
  • Asegurar que los endpoints de error también devuelvan cabeceras CORS válidas.

Ejemplo con Playwright:

test('bloquea origen no autorizado', async ({ request }) => {
	const res = await request.get('https://api.misitio.com/data', {
		headers: { Origin: 'https://malicious.com' }
	});
	expect(res.status()).toBe(403);
});

CORS y OAuth2 / OpenID Connect

Durante los flujos de autenticación (especialmente Authorization Code Flow), los redireccionamientos entre dominios requieren configuraciones CORS y de redirect URI sincronizadas. Si no se gestionan correctamente, el navegador bloqueará las respuestas o tokens.

Recomendaciones:

  • Mantener los redirect_uri registrados en el proveedor de identidad.
  • Usar dominios consistentes en frontend y backend (por ejemplo, subdominios compartidos).
  • No exponer tokens en respuestas sin CORS adecuado.

CORS en Serverless Functions

En plataformas como Vercel, Netlify Functions o AWS Lambda, CORS debe configurarse manualmente en cada función o mediante middleware global.

Ejemplo en Vercel:

export default function handler(req, res) {
	res.setHeader('Access-Control-Allow-Origin', 'https://frontend.com');
	res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
	if (req.method === 'OPTIONS') return res.status(204).end();
	res.status(200).json({ message: 'OK' });
}

Monitoreo y Auditoría

Para garantizar seguridad continua:

  • Analizar logs de acceso rechazado.
  • Implementar métricas sobre orígenes más frecuentes.
  • Integrar alertas cuando se detecten solicitudes repetidas de orígenes desconocidos.

Integración con herramientas de observabilidad como Grafana o Elastic Stack para visualizar tráfico bloqueado por CORS.

Enfoque de Diseño Seguro

  • Definir políticas CORS desde el diseño inicial del sistema.
  • Mantener consistencia entre entornos (dev, staging, prod).
  • Integrar CORS en revisiones de seguridad y pentesting.
  • Documentar claramente las políticas de acceso en la API.

En resumen, hemos cubierto el espectro completo de CORS:

  • Fundamentos técnicos y cabeceras.
  • Implementación en Backend, proxies y nubes.
  • Integraciones con protocolos y arquitecturas modernas.
  • Estrategias de seguridad, auditoría y pruebas automatizadas.

Con esta extensión, el tema CORS queda completamente desarrollado en todos sus niveles prácticos, conceptuales y de aplicación real.