Saltar al contenido principal

🌐 ¿Qué es CORS?

📖 Definición

CORS (Cross-Origin Resource Sharing, Intercambio de Recursos de Origen Cruzado) es un mecanismo de seguridad que indica a los navegadores que permitan el acceso a recursos de diferentes orígenes (origin). Los navegadores restringen las solicitudes de recursos de origen cruzado por defecto por razones de seguridad, y CORS es el método para permitir esto de forma segura.

🎯 Comprender con Analogía

Sistema de Seguridad de Apartamentos

Imagina que vives en el Edificio A:

  • Mismo Origen (Same-Origin): Llamar desde A-101 a oficina A → Libremente permitido ✅
  • Origen Cruzado (Cross-Origin): Llamar desde A-101 a oficina B → Se requiere verificación de seguridad 🔒
  • Configuración CORS: Edificio B dice "También aceptaré llamadas del Edificio A" → Llamada permitida ✅

CORS es así, ¡es el permiso necesario para acceder a recursos de otros edificios (orígenes)!

⚙️ Cómo Funciona

1. ¿Qué es Origen (Origin)?

Origen es la combinación de protocolo, dominio y puerto:

https://www.example.com:443/page
│ │ │ │ │
│ │ │ │ └─ Ruta (no relacionada con origen)
│ │ │ └────── Puerto (443 si se omite)
│ │ └────────────────── Dominio
│ └────────────────────── Protocolo
└───────────────────────────── Origen (Origin)

2. Mismo Origen vs Origen Cruzado

// Página actual: https://www.example.com

Mismo Origen (Same-Origin)
- https://www.example.com/page
- https://www.example.com/api/users

Origen Cruzado (Cross-Origin)
- http://www.example.com // Protocolo diferente
- https://api.example.com // Dominio diferente
- https://www.example.com:8080 // Puerto diferente

3. Proceso de Solicitud CORS

Simple Request (Solicitud Simple)

1. Navegador → Servidor: Enviar solicitud
GET https://api.example.com/data
Origin: https://www.mysite.com

2. Servidor → Navegador: Respuesta
Access-Control-Allow-Origin: https://www.mysite.com

3. Navegador: Verificar respuesta
- Si origen permitido → Entregar datos ✅
- Si no permitido → Error CORS ❌

Preflight Request (Solicitud Preflight)

1. Navegador → Servidor: Pre-verificación (OPTIONS)
OPTIONS https://api.example.com/data
Origin: https://www.mysite.com
Access-Control-Request-Method: POST

2. Servidor → Navegador: Respuesta de permiso
Access-Control-Allow-Origin: https://www.mysite.com
Access-Control-Allow-Methods: POST, GET
Access-Control-Max-Age: 3600

3. Navegador → Servidor: Solicitud real (POST)
POST https://api.example.com/data

4. Servidor → Navegador: Respuesta de datos

💡 Ejemplos Reales

Ocurrencia de Error CORS

// Frontend (https://www.mysite.com)
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

// ❌ Error en consola
// Access to fetch at 'https://api.example.com/users' from origin
// 'https://www.mysite.com' has been blocked by CORS policy:
// No 'Access-Control-Allow-Origin' header is present on the
// requested resource.

Habilitar CORS en el Servidor

Express.js (Node.js)

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

// Método 1: Permitir todos los orígenes (desarrollo)
app.use(cors());

// Método 2: Permitir origen específico (producción recomendado)
app.use(cors({
origin: 'https://www.mysite.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // Incluir cookies
maxAge: 3600 // Tiempo de caché Preflight (segundos)
}));

// Método 3: Permitir múltiples orígenes
const allowedOrigins = [
'https://www.mysite.com',
'https://admin.mysite.com'
];

app.use(cors({
origin: function(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));

// Endpoint API
app.get('/api/users', (req, res) => {
res.json({ users: ['Alice', 'Bob'] });
});

app.listen(3000);

Configuración Manual de Cabeceras

app.use((req, res, next) => {
// Permitir origen
res.header('Access-Control-Allow-Origin', 'https://www.mysite.com');

// Permitir métodos
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');

// Permitir cabeceras
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');

// Permitir información de autenticación
res.header('Access-Control-Allow-Credentials', 'true');

// Manejar solicitud Preflight
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}

next();
});

Python Flask

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

# Permitir todos los orígenes
CORS(app)

# Permitir solo origen específico
CORS(app, resources={
r"/api/*": {
"origins": ["https://www.mysite.com"],
"methods": ["GET", "POST"],
"allow_headers": ["Content-Type"]
}
})

@app.route('/api/users')
def get_users():
return {'users': ['Alice', 'Bob']}

Configuración Nginx

server {
listen 80;
server_name api.example.com;

location /api {
# Añadir cabeceras CORS
add_header 'Access-Control-Allow-Origin' 'https://www.mysite.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;

# Manejar solicitud Preflight
if ($request_method = 'OPTIONS') {
return 204;
}

proxy_pass http://backend;
}
}

🤔 Preguntas Frecuentes

Q1. ¿Por qué se necesita CORS?

R: Por seguridad. Sin CORS:

// Sitio malicioso (evil.com)
fetch('https://bank.com/api/transfer', {
method: 'POST',
credentials: 'include', // Incluye cookies del banco del usuario
body: JSON.stringify({
to: 'hacker-account',
amount: 1000000
})
});

// ¡CORS bloquea tales ataques!

Q2. ¿Cómo resolver errores CORS?

R: Dependiendo de la situación:

// 1. Si controlas el servidor → Añadir cabeceras CORS (recomendado)
app.use(cors({ origin: 'https://frontend.com' }));

// 2. Si no controlas el servidor → Usar servidor proxy
// package.json (Create React App)
{
"proxy": "https://api.example.com"
}

// 3. Entorno de desarrollo → Middleware proxy
// setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
app.use('/api', createProxyMiddleware({
target: 'https://api.example.com',
changeOrigin: true
}));
};

// 4. Extensión de navegador (¡solo desarrollo!)
// Usar extensiones como "CORS Unblock"
// ⚠️ ¡Nunca usar en producción!

Q3. ¿Cuándo usar credentials: 'include'?

R: Al enviar cookies o información de autenticación:

// Frontend
fetch('https://api.example.com/profile', {
credentials: 'include' // Incluir cookies
});

// Backend - Debe especificar origen explícitamente
app.use(cors({
origin: 'https://www.mysite.com', // ¡No puede usar '*'!
credentials: true
}));

// ❌ Ejemplo incorrecto
app.use(cors({
origin: '*', // Comodín
credentials: true // ¡No se puede usar con credentials!
}));

Q4. ¿Diferencia entre Simple Request y Preflight Request?

R: Depende de las condiciones:

// ✅ Simple Request (solicitud directa)
// - Método: GET, HEAD, POST
// - Cabeceras: Solo cabeceras básicas como Accept, Content-Type
// - Content-Type: text/plain, multipart/form-data,
// application/x-www-form-urlencoded

fetch('https://api.example.com/data', {
method: 'GET'
});

// ⚠️ Preflight Request (solicitud después de preflight)
// - Método: PUT, DELETE, PATCH
// - Cabeceras personalizadas: Authorization, etc.
// - Content-Type: application/json

fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // Requiere Preflight
'Authorization': 'Bearer token' // Requiere Preflight
},
body: JSON.stringify({ name: 'Alice' })
});

Q5. ¿Diferencia entre CORS y CSRF?

R: Conceptos de seguridad completamente diferentes:

CORS (Intercambio de Recursos de Origen Cruzado)
├─ Propósito: Permitir acceso a recursos de origen cruzado
├─ Operación: Navegador bloquea/permite respuestas
└─ Solución: Añadir cabeceras de permiso en servidor

CSRF (Falsificación de Solicitud entre Sitios)
├─ Propósito: Prevenir solicitudes falsificadas
├─ Operación: Sitio malicioso envía solicitudes
└─ Solución: Usar tokens CSRF

🎓 Próximos Pasos

Después de entender CORS, aprende:

  1. ¿Qué es HTTPS? (documento en preparación) - Otra seguridad web importante
  2. Token JWT (documento en preparación) - Método de autenticación API
  3. ¿Qué es una API? - Conceptos básicos de API

Herramientas de Depuración

// Verificar en herramientas de desarrollador del navegador
// Pestaña Network → Seleccionar solicitud → Pestaña Headers

// Request Headers
Origin: https://www.mysite.com
Access-Control-Request-Method: POST

// Response Headers
Access-Control-Allow-Origin: https://www.mysite.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Max-Age: 3600

🎬 Resumen

CORS es un concepto central de seguridad web:

  • Origen: Combinación de protocolo + dominio + puerto
  • Política de Mismo Origen: Bloquea diferentes orígenes por defecto
  • CORS: Permite diferentes orígenes de forma segura
  • Preflight: Pre-verificación para solicitudes complejas

Cuando ocurran errores CORS, ¡no entres en pánico - resuélvelos de forma segura configurando cabeceras apropiadas en el servidor! 🌐✨