Una guía completa para mejorar la seguridad del frontend utilizando la Política de seguridad de contenido (CSP) y el intercambio de recursos de origen cruzado (CORS).
Refuerzo de la seguridad del frontend: Política de seguridad de contenido y CORS
En el panorama digital interconectado actual, la seguridad del frontend es primordial. Las aplicaciones web son cada vez más el blanco de ataques sofisticados, lo que hace que las medidas de seguridad sólidas sean esenciales. Dos componentes críticos de una arquitectura de frontend segura son la Política de seguridad de contenido (CSP) y el intercambio de recursos de origen cruzado (CORS). Esta guía completa proporciona una visión en profundidad de estas tecnologías, ofreciendo ejemplos prácticos e ideas accionables para ayudarle a fortalecer sus aplicaciones web contra las amenazas modernas.
¿Qué es la política de seguridad de contenido (CSP)?
La política de seguridad de contenido (CSP) es una capa adicional de seguridad que ayuda a detectar y mitigar ciertos tipos de ataques, incluidos los ataques de Cross-Site Scripting (XSS) y de inyección de datos. La CSP se implementa mediante el envío por parte del servidor web de un encabezado de respuesta HTTP Content-Security-Policy al navegador. Este encabezado define una lista blanca de fuentes desde las que el navegador tiene permiso para cargar recursos. Al restringir las fuentes de contenido que puede cargar un navegador, la CSP hace que sea significativamente más difícil para los atacantes inyectar código malicioso en su sitio web.
Cómo funciona la CSP
La CSP funciona indicando al navegador que sólo cargue recursos (por ejemplo, scripts, hojas de estilo, imágenes, fuentes) de fuentes aprobadas. Estas fuentes se especifican en el encabezado CSP mediante directivas. Si un navegador intenta cargar un recurso desde una fuente que no está permitida explícitamente, bloqueará la solicitud e informará de una infracción.
Directivas CSP: una visión general completa
Las directivas CSP controlan los tipos de recursos que se pueden cargar desde fuentes específicas. Aquí tienes un desglose de algunas de las directivas más importantes:
- default-src: Especifica la fuente predeterminada para todos los tipos de contenido. Se trata de una directiva de reserva que se aplica cuando no hay otras directivas más específicas.
- script-src: Especifica las fuentes desde las que se pueden cargar los scripts. Esto es crucial para prevenir ataques XSS.
- style-src: Especifica las fuentes desde las que se pueden cargar las hojas de estilo.
- img-src: Especifica las fuentes desde las que se pueden cargar las imágenes.
- font-src: Especifica las fuentes desde las que se pueden cargar las fuentes.
- media-src: Especifica las fuentes desde las que se pueden cargar audio y vídeo.
- object-src: Especifica las fuentes desde las que se pueden cargar los plugins (por ejemplo, Flash). A menudo se establece en 'none' para desactivar los plugins por completo debido a sus riesgos de seguridad inherentes.
- frame-src: Especifica las fuentes desde las que se pueden cargar los frames (por ejemplo, <iframe>).
- connect-src: Especifica las URL a las que el agente de usuario puede conectarse utilizando interfaces de script como XMLHttpRequest, WebSocket y EventSource.
- base-uri: Especifica las URL que se pueden utilizar en el elemento <base> de un documento.
- form-action: Especifica las URL a las que se pueden enviar los envíos de formularios.
- upgrade-insecure-requests: Indica al agente de usuario que actualice automáticamente las peticiones no seguras (HTTP) a peticiones seguras (HTTPS).
- report-uri: Especifica una URL donde el navegador debe enviar informes sobre las infracciones de la CSP. Esta directiva está obsoleta en favor de `report-to`.
- report-to: Especifica un nombre de grupo de informes definido en el encabezado `Report-To`, donde el navegador debe enviar informes sobre las infracciones de la CSP.
Palabras clave de la lista de fuentes de la CSP
Dentro de las directivas CSP, puede utilizar palabras clave de la lista de fuentes para definir las fuentes permitidas. Aquí tienes algunas palabras clave comunes:
- 'self': Permite los recursos del mismo origen (esquema y host) que el documento.
- 'none': No permite recursos de ninguna fuente.
- 'unsafe-inline': Permite el uso de scripts y estilos en línea (por ejemplo, etiquetas <script> y atributos de estilo). Úselo con extrema precaución, ya que debilita significativamente la protección de la CSP contra XSS.
- 'unsafe-eval': Permite el uso de funciones de evaluación de código dinámico como
eval()yFunction(). Úselo con extrema precaución, ya que introduce importantes riesgos de seguridad. - 'unsafe-hashes': Permite controladores de eventos en línea o etiquetas <style> específicas que coinciden con un hash especificado. Requiere compatibilidad con el navegador. Úselo con precaución.
- 'strict-dynamic': Especifica que la confianza otorgada explícitamente a un script presente en el marcado, acompañándolo con un nonce o hash, se propagará a todos los scripts cargados por ese script raíz.
- data: Permite los URI de datos (por ejemplo, imágenes en línea codificadas como base64). Úselo con precaución.
- https:: Permite que los recursos se carguen a través de HTTPS desde cualquier dominio.
- [hostname]: Permite los recursos de un dominio específico (por ejemplo, example.com). También puede especificar un número de puerto (por ejemplo, example.com:8080).
- [scheme]://[hostname]:[port]: Un URI totalmente cualificado, que permite los recursos del esquema, el host y el puerto especificados.
Ejemplos prácticos de CSP
Veamos algunos ejemplos prácticos de encabezados CSP:
Ejemplo 1: CSP básica con 'self'
Esta política sólo permite recursos del mismo origen:
Content-Security-Policy: default-src 'self'
Ejemplo 2: Permitir scripts de un dominio específico
Esta política permite scripts de su propio dominio y de una CDN de confianza:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Ejemplo 3: Desactivación de scripts y estilos en línea
Esta política no permite scripts y estilos en línea, lo que supone una sólida defensa contra XSS:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'
Importante: La desactivación de los scripts en línea requiere la refactorización de su HTML para trasladar los scripts en línea a archivos externos.
Ejemplo 4: Uso de Nonces para scripts en línea
Si debe utilizar scripts en línea, utilice nonces (tokens criptográficamente aleatorios y de un solo uso) para incluir en la lista blanca bloques de scripts en línea específicos. Esto es más seguro que 'unsafe-inline'. El servidor debe generar un nonce único para cada petición e incluirlo tanto en el encabezado CSP como en la etiqueta <script>.
Content-Security-Policy: default-src 'self'; script-src 'nonce-r4nd0mN0nc3'; style-src 'self'
<script nonce="r4nd0mN0nc3"> console.log('Inline script'); </script>
Nota: Recuerde generar un nuevo nonce para cada petición. ¡No reutilice los nonces!
Ejemplo 5: Uso de Hashes para estilos en línea
De forma similar a los nonces, los hashes se pueden utilizar para incluir en la lista blanca bloques <style> en línea específicos. Esto se hace generando un hash SHA256, SHA384 o SHA512 del contenido del estilo.
Content-Security-Policy: default-src 'self'; style-src 'sha256-HASHEDSTYLES'
<style sha256="HASHEDSTYLES"> body { background-color: #f0f0f0; } </style>
Nota: Los hashes son menos flexibles que los nonces, ya que cualquier cambio en el contenido del estilo invalidará el hash.
Ejemplo 6: Notificación de infracciones de la CSP
Para supervisar las infracciones de la CSP, utilice la directiva report-uri o report-to:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
También tendrá que configurar el encabezado Report-To. El encabezado Report-To define uno o más grupos de informes, que especifican dónde y cómo deben enviarse los informes.
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Prueba e implementación de la CSP
La implementación de la CSP requiere una planificación y pruebas cuidadosas. Empiece con una política restrictiva y relájela gradualmente según sea necesario. Utilice el encabezado Content-Security-Policy-Report-Only para probar su política sin bloquear recursos. Este encabezado informa de las infracciones sin aplicar la política, lo que le permite identificar y solucionar los problemas antes de implementar la política en producción.
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint;
Analice los informes generados por el navegador para identificar cualquier infracción y ajuste su política en consecuencia. Una vez que esté seguro de que su política funciona correctamente, impleméntela utilizando el encabezado Content-Security-Policy.
Buenas prácticas para la CSP
- Empiece con un default-src: Defina siempre un
default-srcpara establecer una política de base. - Sea específico: Utilice directivas específicas y palabras clave de la lista de fuentes para limitar el alcance de su política.
- Evite 'unsafe-inline' y 'unsafe-eval': Estas palabras clave debilitan significativamente la CSP y deben evitarse siempre que sea posible.
- Utilice nonces o hashes para scripts y estilos en línea: Si debe utilizar scripts o estilos en línea, utilice nonces o hashes para incluir en la lista blanca bloques de código específicos.
- Supervise las infracciones de la CSP: Utilice la directiva
report-urioreport-topara supervisar las infracciones de la CSP y ajustar su política en consecuencia. - Pruebe a fondo: Utilice el encabezado
Content-Security-Policy-Report-Onlypara probar su política antes de implementarla en producción. - Itere y perfeccione: La CSP no es una configuración única. Supervise y perfeccione continuamente su política para adaptarla a los cambios en su aplicación y al panorama de amenazas.
¿Qué es el intercambio de recursos de origen cruzado (CORS)?
El intercambio de recursos de origen cruzado (CORS) es un mecanismo que permite a las páginas web de un origen (dominio) acceder a recursos de un origen diferente. De forma predeterminada, los navegadores aplican una política del mismo origen, que impide que los scripts realicen solicitudes a un origen diferente al del que procede el script. CORS proporciona una forma de relajar selectivamente esta restricción, permitiendo las peticiones legítimas de origen cruzado al tiempo que protege contra los ataques maliciosos.
Comprender la política del mismo origen
La política del mismo origen es un mecanismo de seguridad fundamental que impide que un script malicioso de un sitio web acceda a datos confidenciales de otro sitio web. Un origen se define por el esquema (protocolo), el host (dominio) y el puerto. Dos URL tienen el mismo origen si y sólo si tienen el mismo esquema, host y puerto.
Por ejemplo:
https://www.example.com/app1/index.htmlyhttps://www.example.com/app2/index.htmltienen el mismo origen.https://www.example.com/index.htmlyhttp://www.example.com/index.htmltienen orígenes diferentes (esquema diferente).https://www.example.com/index.htmlyhttps://sub.example.com/index.htmltienen orígenes diferentes (host diferente).https://www.example.com:8080/index.htmlyhttps://www.example.com:80/index.htmltienen orígenes diferentes (puerto diferente).
Cómo funciona CORS
Cuando una página web realiza una petición de origen cruzado, el navegador envía primero una petición de "preflight" al servidor. La petición de preflight utiliza el método HTTP OPTIONS e incluye encabezados que indican el método HTTP y los encabezados que utilizará la petición real. A continuación, el servidor responde con encabezados que indican si la petición de origen cruzado está permitida.
Si el servidor permite la petición, incluye el encabezado Access-Control-Allow-Origin en la respuesta. Este encabezado especifica los orígenes a los que se les permite acceder al recurso. A continuación, el navegador procede con la petición real. Si el servidor no permite la petición, no incluye el encabezado Access-Control-Allow-Origin y el navegador bloquea la petición.
Encabezados CORS: una mirada detallada
CORS se basa en encabezados HTTP para comunicarse entre el navegador y el servidor. Estos son los encabezados CORS clave:
- Access-Control-Allow-Origin: Especifica los orígenes a los que se les permite acceder al recurso. Este encabezado puede contener un origen específico (por ejemplo,
https://www.example.com), un comodín (*) onull. El uso de*permite las peticiones de cualquier origen, lo que generalmente no se recomienda por razones de seguridad. El uso de `null` sólo es apropiado para las "respuestas opacas" como cuando el recurso se recupera utilizando el protocolo `file://` o un URI de datos. - Access-Control-Allow-Methods: Especifica los métodos HTTP que se permiten para la petición de origen cruzado (por ejemplo,
GET, POST, PUT, DELETE). - Access-Control-Allow-Headers: Especifica los encabezados HTTP que se permiten en la petición de origen cruzado. Esto es importante para gestionar los encabezados personalizados.
- Access-Control-Allow-Credentials: Indica si el navegador debe incluir credenciales (por ejemplo, cookies, encabezados de autorización) en la petición de origen cruzado. Este encabezado debe establecerse en
truepara permitir las credenciales. - Access-Control-Expose-Headers: Especifica qué encabezados se pueden exponer al cliente. De forma predeterminada, sólo se expone un conjunto limitado de encabezados.
- Access-Control-Max-Age: Especifica la cantidad máxima de tiempo (en segundos) que el navegador puede almacenar en caché la petición de preflight.
- Origin: Este es un encabezado de petición enviado por el navegador para indicar el origen de la petición.
- Vary: Un encabezado HTTP general, pero importante para CORS. Cuando `Access-Control-Allow-Origin` se genera dinámicamente, el encabezado `Vary: Origin` debe incluirse en la respuesta para indicar a los mecanismos de almacenamiento en caché que la respuesta varía en función del encabezado de petición `Origin`.
Ejemplos prácticos de CORS
Veamos algunos ejemplos prácticos de configuraciones CORS:
Ejemplo 1: Permitir peticiones de un origen específico
Esta configuración sólo permite peticiones de https://www.example.com:
Access-Control-Allow-Origin: https://www.example.com
Ejemplo 2: Permitir peticiones de cualquier origen (no recomendado)
Esta configuración permite peticiones de cualquier origen. Úselo con precaución, ya que puede introducir riesgos de seguridad:
Access-Control-Allow-Origin: *
Ejemplo 3: Permitir métodos y encabezados específicos
Esta configuración permite los métodos GET, POST y PUT, y los encabezados Content-Type y Authorization:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Ejemplo 4: Permitir credenciales
Para permitir credenciales (por ejemplo, cookies), debe establecer Access-Control-Allow-Credentials en true y especificar un origen específico (no puede utilizar * cuando permite credenciales):
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
También debe establecer credentials: 'include' en su petición JavaScript fetch/XMLHttpRequest.
fetch('https://api.example.com/data', {
credentials: 'include'
})
Peticiones CORS Preflight
Para ciertos tipos de peticiones de origen cruzado (por ejemplo, peticiones con encabezados personalizados o métodos distintos de GET, HEAD o POST con Content-Type de application/x-www-form-urlencoded, multipart/form-data o text/plain), el navegador envía una petición de preflight utilizando el método OPTIONS. El servidor debe responder a la petición de preflight con los encabezados CORS apropiados para indicar si la petición real está permitida.
Aquí tienes un ejemplo de una petición y respuesta de preflight:
Petición de preflight (OPTIONS):
OPTIONS /data HTTP/1.1
Origin: https://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
Respuesta de preflight (200 OK):
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
El encabezado Access-Control-Max-Age especifica cuánto tiempo puede el navegador almacenar en caché la respuesta de preflight, reduciendo el número de peticiones de preflight.
CORS y JSONP
JSON with Padding (JSONP) es una técnica más antigua para eludir la política del mismo origen. Sin embargo, JSONP tiene importantes riesgos de seguridad y debe evitarse en favor de CORS. JSONP se basa en la inyección de etiquetas <script> en la página, que pueden ejecutar código arbitrario. CORS proporciona una forma más segura y flexible de gestionar las peticiones de origen cruzado.
Buenas prácticas para CORS
- Evite usar *: Evite usar el comodín (*) en el encabezado
Access-Control-Allow-Origin, ya que permite peticiones de cualquier origen. En su lugar, especifique el origen u orígenes específicos a los que se les permite acceder al recurso. - Sea específico con los métodos y encabezados: Especifique los métodos y encabezados HTTP exactos que se permiten en los encabezados
Access-Control-Allow-MethodsyAccess-Control-Allow-Headers. - Utilice Access-Control-Allow-Credentials con precaución: Sólo active
Access-Control-Allow-Credentialssi necesita permitir credenciales (por ejemplo, cookies) en peticiones de origen cruzado. Tenga en cuenta las implicaciones de seguridad de permitir las credenciales. - Asegure sus peticiones de preflight: Asegúrese de que su servidor gestiona correctamente las peticiones de preflight y devuelve los encabezados CORS correctos.
- Utilice HTTPS: Utilice siempre HTTPS tanto para el origen como para los recursos a los que accede de forma cruzada. Esto ayuda a proteger contra los ataques man-in-the-middle.
- Vary: Origin: Si está generando dinámicamente el encabezado `Access-Control-Allow-Origin`, incluya siempre el encabezado `Vary: Origin` para evitar problemas de almacenamiento en caché.
CSP y CORS en la práctica: un enfoque combinado
Aunque CSP y CORS abordan las preocupaciones de seguridad, operan en diferentes capas y proporcionan protección complementaria. CSP se centra en evitar que el navegador cargue contenido malicioso, mientras que CORS se centra en controlar qué orígenes pueden acceder a los recursos de su servidor.
Combinando CSP y CORS, puede crear una postura de seguridad más robusta para sus aplicaciones web. Por ejemplo, puede utilizar CSP para restringir las fuentes desde las que se pueden cargar los scripts, y CORS para controlar qué orígenes pueden acceder a sus puntos finales de la API.
Ejemplo: Protección de una API con CSP y CORS
Digamos que tiene una API alojada en https://api.example.com que quiere que sea accesible sólo desde https://www.example.com. Puede configurar su servidor para que devuelva los siguientes encabezados:
Encabezados de respuesta de la API (https://api.example.com):
Access-Control-Allow-Origin: https://www.example.com
Content-Type: application/json
Y puede configurar su sitio web (https://www.example.com) para que utilice el siguiente encabezado CSP:
Encabezado CSP del sitio web (https://www.example.com):
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com;
Esta política CSP permite que el sitio web cargue scripts y se conecte a la API, pero le impide cargar scripts o conectarse a otros dominios.
Conclusión
La política de seguridad de contenido (CSP) y el intercambio de recursos de origen cruzado (CORS) son herramientas esenciales para reforzar la seguridad de sus aplicaciones frontend. Al configurar cuidadosamente CSP y CORS, puede reducir significativamente el riesgo de ataques XSS, ataques de inyección de datos y otras vulnerabilidades de seguridad. Recuerde empezar con una política restrictiva, probar a fondo y supervisar y perfeccionar continuamente su configuración para adaptarla a los cambios en su aplicación y al panorama de amenazas en evolución. Al priorizar la seguridad del frontend, puede proteger a sus usuarios y garantizar la integridad de sus aplicaciones web en el mundo digital cada vez más complejo de hoy.