Implicaciones de seguridad del uso compartido de recursos entre orígenes (CORS) en Node.js
Victor Ikechukwu
13 de septiembre de 2023
0 minutos de lecturaEn las aplicaciones web modernas, el uso compartido de recursos entre orígenes (CORS) permite la comunicación segura entre aplicaciones alojadas en distintos orígenes. Los desarrolladores usan CORS para acceder a los servicios de otras aplicaciones desde las suyas. Este enfoque evita tener que volver a crear funciones desde cero, acelera el desarrollo y mejora la experiencia de los desarrolladores.
Aunque CORS es útil, una implementación incorrecta puede exponer tus aplicaciones de Node.js a riesgos de seguridad, como filtraciones de datos y accesos no autorizados desde sitios web de terceros. Las configuraciones incorrectas pueden revelar datos confidenciales a orígenes no previstos o permitir que sitios web maliciosos evadan las protecciones de la política del mismo origen (SOP).
Comprender estas vulnerabilidades y adoptar prácticas recomendadas para implementar CORS de forma segura ayuda a mitigar estos riesgos y también te permite mantener la funcionalidad de tu aplicación.
En este artículo, primero veremos qué es CORS y algunos de sus casos de uso. Después, usaremos un ejemplo de código para implementar CORS en una aplicación de Node.js. Tras analizar los posibles problemas de seguridad, repasaremos las prácticas recomendadas para usar CORS y probaremos la seguridad de nuestro ejemplo. Para seguir el tutorial, solo necesitas algo de experiencia con JavaScript y Node.js.
CORS y sus casos de uso
Los navegadores web usan un mecanismo de seguridad llamado política del mismo origen (SOP) para regular la interacción entre aplicaciones web. La SOP impide que las aplicaciones alojadas en un origen lean los recursos de una aplicación alojada en un origen diferente.
Este mecanismo impide que sitios maliciosos lean los datos de otros sitios, pero también puede restringir usos legítimos. ¿Qué pasa si quieres incluir datos meteorológicos en tu aplicación? ¿O insertar un video de YouTube en una página web? Todos deberían poder leer estos recursos públicos, pero la SOP los bloquea.
CORS permite que las aplicaciones web superen las limitaciones de la SOP, facilita la comunicación entre distintos servicios web y permite solicitudes entre orígenes. Las aplicaciones pueden usar el método HTTP OPTIONS para enviar solicitudes de comprobación previa al recurso del otro origen. Mediante estas solicitudes, los navegadores web determinan si el servidor del otro origen las permite. Con base en esa información, el navegador ignora o aplica la SOP.
Entre los casos de uso típicos de CORS se incluyen cargar recursos desde redes de distribución de contenido (CDN), solicitar API en varios dominios y facilitar la integración con terceros.
Sin embargo, implementar CORS de forma incorrecta en tu aplicación implica riesgos de seguridad. Podrías exponerla a un ataque de falsificación de solicitudes entre sitios (CSRF), que engaña a los usuarios para que realicen acciones no deseadas en la aplicación. Además, las configuraciones demasiado permisivas pueden permitir el acceso no autorizado a datos de usuarios e información confidencial entre orígenes, lo que podría provocar filtraciones de datos o exponer vulnerabilidades que los atacantes podrían aprovechar.
Por ejemplo, podrías sentirte tentado a usar el comodín permitir todo, representado por un asterisco (*), como valor del encabezado HTTP Access-Control-Allow-Origin. Esta configuración de CORS permite que cualquier origen interactúe con tu aplicación y la expone a posibles vulnerabilidades. En su lugar, elige configuraciones específicas y restrictivas. Para definir dinámicamente los orígenes permitidos, considera usar variables de entorno como alternativa más segura.
Implementar CORS de forma segura en una aplicación de Node.js
En esta sección, veremos cómo implementar CORS de forma segura en una aplicación de Node.js. Usaremos una aplicación sencilla de Node.js que utiliza Express.js como framework web. La aplicación funcionará como API para una librería en línea hipotética.
Aunque la API tiene varios endpoints (para fetch, add, update y delete libros), este ejemplo solo implementará la función de obtener libros. Para simplificar, la API se conectará a un objeto sampleBooksData que sirve como sistema de almacenamiento de datos.
Requisitos previos
Para seguir este tutorial, necesitas lo siguiente:
Experiencia con JavaScript
Node.js (se recomienda la versión 18.16.1)
Un navegador web moderno
Crear la aplicación de Node.js
Para crear la aplicación, primero crea un directorio nuevo. Luego, ejecuta el siguiente comando en ese directorio para inicializar una aplicación de Node.js:
A continuación, ejecuta el siguiente comando para instalar las dependencias de este proyecto:
Por último, crea un archivo JavaScript y asígnale el nombre index.js. Pega el siguiente código:
Express.js ofrece funciones de middleware para procesar solicitudes y respuestas dentro de la aplicación. Estas funciones pueden interceptar y modificar solicitudes entrantes o respuestas salientes, y permiten habilitar funciones como el registro, la autenticación o, en nuestro caso, la configuración de CORS. El ejemplo anterior importa el módulo CORS con const cors = require("cors");. Aplica el módulo como función de middleware a la ruta /books mediante el siguiente código:
Sin embargo, la implementación actual del middleware cors en la ruta /books no especifica explícitamente qué orígenes pueden hacer una solicitud GET. Por eso, la configuración permite que todos los orígenes hagan solicitudes a esa ruta. Por motivos de seguridad, no es una buena idea, pero puedes configurar reglas más restrictivas para tu entorno de producción.
Configurar CORS
Puedes implementar CORS de forma más segura con el middleware cors mediante cuatro configuraciones diferentes.
Habilitar CORS para orígenes específicos
Puedes establecer la opción origin en la configuración de CORS para definir los orígenes permitidos. Esto mejora la seguridad al restringir el acceso a fuentes confiables e impedir solicitudes no autorizadas entre orígenes. El siguiente ejemplo solo permite solicitudes desde `https://example.com`:
Configurar los métodos HTTP permitidos
También puedes definir los métodos permitidos con la opción methods. Este enfoque limita los posibles vectores de ataque al permitir únicamente las acciones necesarias en los endpoints de tu API. El siguiente ejemplo solo permite las acciones GET y POST:
Definir encabezados personalizados y encabezados expuestos
Otra opción es configurar el encabezado CORS Access-Control-Allow-Headers mediante la opción allowedHeaders y Access-Control-Expose-Headers mediante la opción exposedHeaders. Este enfoque permite que la aplicación gestione correctamente los datos confidenciales y mantenga una comunicación flexible entre los componentes del cliente y del servidor. El siguiente ejemplo solo permite los encabezados Content-Type y Authorization, y solo expone X-Custom-Header:
Configurar las solicitudes de comprobación previa y el almacenamiento en caché
Por último, puedes habilitar el almacenamiento en caché de las solicitudes de comprobación previa (OPTIONS) mediante la configuración de la duración de la caché con maxAge. Así, el cliente puede guardar en caché la información sobre la política CORS del servidor durante un tiempo determinado. En las solicitudes posteriores, los clientes pueden usar los datos de la política CORS guardados en caché sin volver a obtenerlos del servidor.
Establecer un valor adecuado para maxAge reduce el tiempo de respuesta y el tráfico de red, lo que optimiza el rendimiento y mantiene actualizadas las políticas de CORS. El siguiente ejemplo establece maxAge en 86400 segundos (24 horas):
Ahora que configuramos las opciones, podemos usar nuestro código como middleware en cualquier ruta que requiera CORS:
Aunque CORS ayuda a proteger la API, sigue siendo una dependencia de terceros. Debemos asegurarnos de que esta y las demás dependencias que agreguemos al proyecto sean seguras y no tengan vulnerabilidades conocidas. Por ejemplo, Snyk Open Source ayuda a detectar problemas de seguridad en los paquetes durante el desarrollo o en los pipelines de integración y entrega continuas (CI/CD).
Implicaciones de seguridad de CORS y prácticas recomendadas
Si no configuramos CORS correctamente, corremos el riesgo de provocar las vulnerabilidades de seguridad mencionadas antes. En concreto, podríamos exponer datos confidenciales a orígenes no previstos, permitir que sitios web maliciosos realicen acciones no autorizadas (ataques CSRF) e incluso permitir que los atacantes evadan las protecciones de la SOP mediante técnicas como la secuencia de comandos entre sitios (XSS).
Para evitar estas situaciones, sigue siempre las prácticas recomendadas al implementar configuraciones de CORS. También puedes tomar otras medidas de seguridad, como implementar encabezados que no formen parte de CORS, tema que veremos más adelante en esta sección.
Prácticas recomendadas
Aplicar prácticas recomendadas como restringir los orígenes permitidos, usar cookies y tokens seguros y limitar los encabezados expuestos es fundamental para proteger tus aplicaciones y a sus usuarios. A continuación, veremos estas prácticas con ejemplos de código que te mostrarán cómo aplicarlas.
Restringir los orígenes permitidos mediante una lista de permitidos
Para evitar usar el comodín que permite todos los orígenes (*) en el objeto corsOptions, especifica los orígenes en una lista de permitidos:
Usar cookies y tokens seguros para la autenticación
Usa el protocolo HTTPS para asegurarte de que tu aplicación transmita cookies y tokens de forma segura. También usa bibliotecas de autenticación confiables, como Passport.js, o soluciones de JSON Web Token (JWT):
En este código, establecer el valor de credentials to true permite que las solicitudes entre orígenes incluyan credenciales.
Limitar los encabezados expuestos y los métodos HTTP
Para especificar solo los encabezados esenciales, usa la opción exposedHeaders. Además, usa la opción methods en tu configuración para restringir los métodos que habilitan CORS:
Implementar encabezados de seguridad fuera de CORS
Puedes implementar otros encabezados de seguridad fuera de CORS para proteger aún más tu aplicación. Los encabezados de seguridad HTTP indican los aspectos de seguridad de la comunicación HTTP entre un cliente y un servidor.
Helmet.js ofrece un conjunto de encabezados de seguridad con valores predeterminados adecuados. Sin embargo, requiere cierta configuración de la política de seguridad del contenido (CSP) para habilitar la funcionalidad definida en la configuración de CORS. Sigue estos pasos para configurarla.
Primero, ejecuta el siguiente comando en la línea de comandos del directorio de tu aplicación de Node.js para instalar Helmet:
A continuación, importa el paquete a tu aplicación con el siguiente código:
Después, configura la CSP para que coincida con tu configuración de CORS, como se muestra a continuación:
Por último, revisa tu trabajo. Es fácil pasar por alto una configuración o cometer un error tipográfico que comprometa la seguridad de tu aplicación, sobre todo a medida que crece. Snyk Code y las extensiones de herramientas para desarrolladores de Snyk (como la extensión de Visual Studio Code) detectan encabezados de seguridad faltantes o mal configurados en tu base de código. El uso de estas herramientas de terceros para abordar de forma proactiva los problemas de seguridad durante el desarrollo ayuda a mantener segura tu aplicación de Node.js.
Probar las implementaciones de CORS y su seguridad
Para asegurarte de que tu aplicación esté protegida frente a posibles amenazas, prueba la implementación de CORS y su seguridad. Esto ayuda a identificar posibles vulnerabilidades y configuraciones incorrectas. Las pruebas también verifican que las reglas de CORS funcionen según lo previsto y no expongan la aplicación a riesgos de seguridad, como filtraciones de datos o accesos no autorizados desde sitios web de terceros.
Hay varias formas de probar las implementaciones de CORS y su seguridad. A continuación, veremos algunos métodos populares.
Herramientas para desarrolladores del navegador
Los navegadores web modernos incluyen herramientas para desarrolladores que permiten inspeccionar y depurar aplicaciones web. Puedes usarlas para probar CORS examinando las solicitudes y respuestas de red. Simula solicitudes entre orígenes para verificar que las reglas de CORS las bloqueen o permitan según corresponda.
Más adelante ampliaremos este método y lo aplicaremos a la aplicación de Node.js de ejemplo que creamos antes.
Postman
Puedes usar Postman, la popular herramienta de desarrollo y pruebas de API, para probar CORS. Te permite crear solicitudes HTTP y especificar encabezados personalizados para probar las configuraciones de CORS. Postman también puede generar automáticamente solicitudes de verificación previa para probar el almacenamiento en caché de estas solicitudes. Además, puedes usar las funciones de scripting de Postman para automatizar escenarios de prueba.
Scripts personalizados
Puedes crear scripts personalizados para probar las configuraciones de CORS. Por ejemplo, usa un lenguaje de programación como Python o JavaScript para enviar solicitudes HTTP y verificar que las respuestas incluyan encabezados de CORS. También puedes simular solicitudes de origen cruzado y verificar que las reglas de CORS bloqueen o permitan las solicitudes según corresponda.
Cómo probar la configuración de CORS con las herramientas para desarrolladores del navegador
Puedes probar las configuraciones de CORS en nuestra aplicación de ejemplo de Node.js con las herramientas para desarrolladores del navegador. Sigue estos pasos.
Primero, inicia la aplicación de Node.js. En la línea de comandos del directorio de la aplicación, ejecuta el siguiente comando:
Luego, ve a la página principal de Google en el navegador que prefieras.

A continuación, abre la consola para desarrolladores. Si usas un navegador basado en Chromium, usa el comando Option+⌘+J (en macOS) o Shift+CTRL+ J (en Windows/Linux). También puedes usar los comandos correspondientes en el navegador que prefieras.
Pega el código siguiente y presiona Return/Enter:
Según la configuración actual de CORS de la aplicación de ejemplo, lo más probable es que recibas errores que indiquen que la política de CORS bloqueó el acceso del origen (Google) al recurso /books en tu servidor y que se produjo un error interno del servidor.

El error de acceso bloqueado se debe a que el origen https://www.google.com no está entre los orígenes autorizados para superar la SOP. La configuración de la opción `origin` del objeto corsOptions que pasamos al middleware cors no permite que ese origen reciba una respuesta del servidor. Ten en cuenta que, aunque la vista y el mensaje de error pueden variar según el navegador, el error seguirá indicando que la solicitud está bloqueada.
Este error confirma que las reglas de implementación de CORS funcionan según lo previsto. No se puede acceder a la aplicación desde orígenes no autorizados.
Para aprobar la solicitud de https://www.google.com, especifícalo en la opción origin del objeto corsOptions:
Cuando reinicies el servidor y vuelvas a ejecutar el comando fetch en la consola para desarrolladores, debería funcionar según lo previsto. Si sigues recibiendo el error, es posible que debas ejecutar el script en otro navegador. Para que el comando fetch funcione en el navegador Brave, deberás desactivar Shields. Ten en cuenta que la configuración actual de CORS puede fallar al usar el navegador Safari. Esto se debe a que Safari aplica estrictamente la Política del mismo origen (SOP), que restringe las solicitudes de otros orígenes.

Permitimos https://www.google.com en la configuración de CORS, así que obtenemos una lista de libros en lugar de errores.
Próximos pasos
Aunque CORS agrega funciones útiles a tu aplicación, una configuración incorrecta puede exponerla a vulnerabilidades de seguridad. Debido a estos riesgos, debes aplicar las prácticas recomendadas siempre que sea posible para proteger tus aplicaciones de Node.js contra posibles amenazas.
Puedes configurar el middleware cors con orígenes, métodos HTTP, encabezados y solicitudes de verificación previa específicos para lograr una comunicación segura entre orígenes. Herramientas como Snyk Code y las herramientas para desarrolladores del navegador también te ayudan a encontrar de forma proactiva errores y vulnerabilidades durante el desarrollo. Este enfoque ayuda a garantizar que tu aplicación sea segura al pasar a producción y mantiene protegidos tus sistemas y usuarios.
Una vez que hayas configurado los ajustes de CORS siguiendo estas prácticas recomendadas, prueba Snyk Code para analizar tu aplicación en tiempo real y verificar dos veces tus ajustes de seguridad. Puedes realizar 200 pruebas al mes gratis.
Comienza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.



