¿Qué son los CORS y para qué sirven?
CORS (Cross-Origin Resource Sharing) es un mecanismo de seguridad utilizado en aplicaciones web para controlar cómo se permite que un navegador acceda a recursos en un servidor que tiene un dominio diferente (origen) al del sitio web que realiza la solicitud.
- Origen cruzado: Cuando una página web de un dominio intenta acceder a un recurso (API, archivo, etc.) ubicado en otro dominio.
-
Finalidad de CORS:
- Proteger contra ataques de tipo CSRF (Cross-Site Request Forgery).
- Permitir el intercambio de recursos específicos entre diferentes dominios de forma segura.
- Definir reglas de acceso mediante encabezados HTTP para que el servidor indique qué solicitudes de origen cruzado son permitidas.
Por ejemplo:
-
El navegador de
http://example.com
quiere acceder a datos desdehttp://api.example2.com
. - Sin CORS, los navegadores bloquearán estas solicitudes por políticas de seguridad.
Encabezados clave en CORS:
-
Access-Control-Allow-Origin
:- Especifica qué dominios tienen permiso para acceder al recurso.
-
Ejemplo:
Access-Control-Allow-Origin: *
(permite acceso desde todos los dominios).
-
Access-Control-Allow-Methods
:-
Lista de métodos HTTP permitidos para la solicitud (ej.,
GET
,POST
).
-
Lista de métodos HTTP permitidos para la solicitud (ej.,
-
Access-Control-Allow-Headers
:- Define los encabezados HTTP permitidos por el servidor.
-
Access-Control-Max-Age
:- Especifica cuánto tiempo un cliente puede usar una respuesta en caché antes de realizar una nueva solicitud.
Código en lenguaje C para implementar soporte básico de CORS
A continuación, se presenta el código fuente avanzado de un servidor HTTP en C con soporte para CORS. Este servidor incluye encabezados CORS según estándares de seguridad y telecomunicaciones.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
void handle_request(int client_socket);
void send_response(int client_socket, const char *status, const char *content_type, const char *body, const char *cors_origin);
int main() {
int server_fd, client_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
// Crear socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed");
exit(EXIT_FAILURE);
}
// Configurar dirección
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Asociar socket
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
// Escuchar conexiones
if (listen(server_fd, 3) < 0) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Servidor HTTP con soporte CORS ejecutándose en el puerto %d\n", PORT);
// Manejar clientes
while (1) {
if ((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Error al aceptar conexión");
continue;
}
handle_request(client_socket);
close(client_socket);
}
return 0;
}
void handle_request(int client_socket) {
char buffer[BUFFER_SIZE] = {0};
read(client_socket, buffer, BUFFER_SIZE);
// Imprimir solicitud recibida (opcional para depuración)
printf("Solicitud recibida:\n%s\n", buffer);
// Verificar si es una solicitud OPTIONS (verificación previa de CORS)
if (strncmp(buffer, "OPTIONS", 7) == 0) {
// Enviar respuesta para solicitudes preflight (verificación previa)
send_response(client_socket, "204 No Content", NULL, NULL, "*");
} else {
// Enviar respuesta estándar con encabezados CORS
const char *body = "<html><body><h1>Bienvenido al servidor CORS</h1></body></html>";
send_response(client_socket, "200 OK", "text/html", body, "*");
}
}
void send_response(int client_socket, const char *status, const char *content_type, const char *body, const char *cors_origin) {
char response[BUFFER_SIZE] = {0};
// Encabezados HTTP básicos
snprintf(response, sizeof(response),
"HTTP/1.1 %s\r\n"
"Access-Control-Allow-Origin: %s\r\n" // Permitir acceso desde cualquier origen
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Headers: Content-Type\r\n"
"Access-Control-Max-Age: 86400\r\n", // Mantener respuestas preflight en caché por 1 día
status, cors_origin);
if (content_type && body) {
// Agregar encabezados de contenido
strncat(response, "Content-Type: ", sizeof(response) - strlen(response) - 1);
strncat(response, content_type, sizeof(response) - strlen(response) - 1);
strncat(response, "\r\n\r\n", sizeof(response) - strlen(response) - 1);
strncat(response, body, sizeof(response) - strlen(response) - 1);
} else {
// Finalizar para respuesta preflight
strncat(response, "\r\n", sizeof(response) - strlen(response) - 1);
}
// Enviar respuesta al cliente
write(client_socket, response, strlen(response));
}
Características y estándares implementados:
-
Cumple con estándares internacionales:
- El soporte para CORS utiliza los encabezados HTTP definidos por la IETF (RFC 7231 y RFC 6454) para intercambio de recursos entre dominios.
-
Maneja solicitudes preflight (método OPTIONS) y proporciona un caché
para respuestas en hasta 24 horas
(
Access-Control-Max-Age
).
-
Características avanzadas:
-
Permite el acceso desde cualquier origen (
Access-Control-Allow-Origin: *
). -
Soporta métodos estándar como
GET
,POST
yOPTIONS
. - Manejo básico de encabezados personalizados.
-
Permite el acceso desde cualquier origen (
-
Seguridad:
-
Aunque permite el acceso desde cualquier origen, puedes restringirlo
a dominios específicos cambiando
*
por el dominio permitido (por ejemplo,Access-Control-Allow-Origin: https://example.com
).
-
Aunque permite el acceso desde cualquier origen, puedes restringirlo
a dominios específicos cambiando
¿Cómo usarlo?
-
Compilar el servidor: Guarda el código en un archivo llamado
cors_server.c
y compílalo con: - Ejecutar el servidor:
- Hacer una solicitud desde la terminal:
- Solicitudes OPTIONS para simulación de preflight:
- Solicitudes normales GET o POST:
gcc -o cors_server cors_server.c
./cors_server
curl -X OPTIONS http://localhost:8080 -i
curl -X GET http://localhost:8080 -i
¿Es viable en proyectos reales?
- Sí, este código cumple con los estándares básicos de telecomunicaciones, informática y seguridad para implementar CORS.
-
Para uso en proyectos reales, debes:
- Implementar HTTPS (por ejemplo, con OpenSSL) para asegurar la transferencia de datos.
- Agregar autenticación y control de acceso más refinado si los recursos son sensibles.
- Extender el soporte para métodos HTTP adicionales, encabezados personalizados o reglas específicas para orígenes.
Este servidor puede integrarse en sistemas de backend modernos o incluso en aplicaciones embebidas para garantizar compatibilidad con aplicaciones web que requieren acceso entre dominios.
Interpretación del resultado
Servidor HTTP con soporte CORS ejecutándose en el puerto 8080
Solicitud recibida: GET / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i
Solicitud recibida: GET /favicon.ico HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Referer: http://localhost:8080/
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site:
same-origin Priority: u=6
General análisis del resultado:
El servidor HTTP con soporte CORS está ejecutándose
correctamente en el puerto 8080
y recibió dos solicitudes
GET:
-
Solicitud para el recurso
/
(página principal del servidor). -
Solicitud para
/favicon.ico
(ícono del sitio web).
Cada solicitud incluye encabezados HTTP que describen información adicional sobre lo que el cliente (navegador Firefox) está solicitando. Estos encabezados son típicos y contienen metadatos sobre la solicitud, como el tipo de contenido, la codificación aceptada, el idioma, y la conexión.
Por qué dice Sec-Fetch-Mode: no-cors
en la segunda solicitud
Concepto de Sec-Fetch-Mode
:
El encabezado Sec-Fetch-Mode
es una característica de
seguridad moderna introducida en navegadores como Firefox y Chrome. Este
encabezado describe cómo el navegador está solicitando el recurso:
-
navigate
:-
Modo que indica una solicitud para navegar directamente al documento
principal (como en la primera solicitud al recurso
/
).
-
Modo que indica una solicitud para navegar directamente al documento
principal (como en la primera solicitud al recurso
-
no-cors
:- Modo utilizado para recursos secundarios, donde el navegador no aplica los métodos CORS (por ejemplo, en solicitudes que no necesitan autorización o encabezados personalizados).
- Este modo es utilizado principalmente para recursos simples (como imágenes, íconos o scripts) que el navegador puede cargar automáticamente sin interactuar con las reglas de origen cruzado (CORS).
Contexto de la solicitud para /favicon.ico
:
-
Por qué
no-cors
:-
El navegador solicita el archivo
favicon.ico
como un recurso pasivo que no requiere autorización o interacción directa con el servidor (ningún encabezado CORS es evaluado porque es un recurso estático). - Estos tipos de solicitudes están diseñados para ser lo más eficientes posible y permiten al navegador descargar el recurso sin verificar reglas de origen cruzado.
-
El navegador solicita el archivo
Secuencia de las solicitudes:
-
Primera solicitud (
GET / HTTP/1.1
):- La solicitud se realizó con el propósito de navegar al recurso principal del servidor (página de inicio).
-
El navegador utiliza
Sec-Fetch-Mode: navigate
porque el recurso solicitado es un documento que el navegador necesita renderizar como parte de la navegación.
-
Segunda solicitud (
GET /favicon.ico HTTP/1.1
):-
Esta es una solicitud automática realizada por el navegador para
cargar el ícono del sitio web (
favicon.ico
). -
Debido a que es un recurso secundario y pasivo, se utiliza el modo
no-cors
.
-
Esta es una solicitud automática realizada por el navegador para
cargar el ícono del sitio web (
Conclusión
-
Primera solicitud: Modo
navigate
porque el navegador está solicitando un documento interactivo. -
Segunda solicitud: Modo
no-cors
porque el navegador está solicitando un recurso pasivo (favicon) que no requiere interacción con las reglas de CORS.