Content Security Policy: mayor seguridad para tus contenidos web

Los contenidos activos en páginas web, como por ejemplo JavaScript, CSS o ActiveX, plantean riesgos de seguridad para los usuarios de Internet y los gestores de páginas web y pueden manipularse, por ejemplo, mediante el Cross Site Scripting. Para que las páginas de Internet puedan utilizarse sin preocupaciones, existe lo que se conoce como Content Security Policy (CSP), cuyo papel consiste en proteger a las páginas frente a ataques perjudiciales y es compatible con la mayoría de navegadores web. Por ello, este concepto de seguridad protege tanto a páginas web como a usuarios de Internet, pero ¿en qué consiste y cómo funciona CSP?

Desarrollo de la Content Security Policy

La Política de seguridad de contenido (CSP, Content Security Policy) tiene sus orígenes en el año 2004, cuando se le conocía con el nombre de “Content Restriction”. Su aparición estuvo motivada por las brechas de seguridad cada vez mayores en los scripts de Internet. El Cross Site Scripting (XSS) en concreto es un método criminal que consiste en infiltrar código malicioso en una página web, por lo que plantea un enorme riesgo para los usuarios. Si bien los usuarios visitan una página en principio fiable, en ella puede haberse insertado un script que carga los datos maliciosos de una fuente externa. Los ciberdelincuentes utilizan para ello, por ejemplo, brechas de seguridad en las funciones de comentarios de la página. Así pueden obtener acceso a ordenadores personales sin que los usuarios de Internet sean conscientes de ello y en la mayoría de los casos, ni siquiera los webmasters suelen darse cuenta de la infiltración de código.

La Mozilla Foundation impulsó el desarrollo de la CSP para solucionar esta problemática. La ventaja del estándar de seguridad es que se pueden establecer reglas en el navegador con respecto a los scripts que el software tiene que cargar y los que no. Para ello, la Content Security Policy se basa en cabeceras HTTP.

Hecho

La Mozilla Foundation es la organización que se encarga del desarrollo del navegador Firefox. Esta organización no lucrativa se ocupa de estructurar los proyectos de Mozilla y de llevar a cabo innovaciones en Internet y fue fundada por los antiguos trabajadores de Netscape, que desarrollaron uno de los primeros navegadores web.

¿Cómo funciona la Content Security Policy?

Para establecer la comunicación en Internet, cliente y servidor intercambian datos a través del Hypertext Transfer Protocol (HTTP). Los campos de las cabeceras HTTP son una parte muy importante de las requests (peticiones) y las responses (respuestas) y en ellas se transmiten parámetros y argumentos relevantes para el intercambio de ambos participantes en la conversación (servidor y cliente). Dichas cabeceras se dividen en diversos campos para solicitudes y respuestas y pueden, por ejemplo, contener información sobre el conjunto de caracteres, el idioma y las cookies. CSP se desarrolla como campo de cabecera de respuesta, lo que significa que el servidor entrega los datos y el navegador los procesa.

El webmaster crea la cabecera de la Content Security Policy y la integra en cada subpágina de la página web en la que deba aplicarse el estándar de seguridad. También cabe la posibilidad de aplicar diferentes medidas de seguridad para cada una de las páginas. Un modo sencillo de implementar este concepto de seguridad es mediante la creación de archivos .htaccessd, que se colocan en las mismas carpetas (o en un nivel más elevado jerárquicamente) que las páginas web o anclando la CSP directamente en la configuración del servidor.

Nota

Si, como usuario de Internet quieres comprobar si tu navegador soporta una Content Security Policy, puedes realizar el CSP browser test. Este intenta cargar scripts e imágenes de fuentes externas (inofensivas) y te muestra si el proceso se realiza con éxito. Si no carga fuentes externas, el navegador está seguro pues entiende y aplica la CSP.

En el marco de la Content Security Policy y para evitar el Cross Site Scripting, los webmasters deben externalizar todos los scripts en archivos separados en lugar de integrarlos directamente en el código fuente de la página web. La CSP funciona según el principio de la whitelist: en el header se mencionan las fuentes desde las que se pueden cargar scripts y datos. Si un script ajeno se introduce en el código HTML sin ser reconocido y este intenta cargar datos externos, el navegador del usuario lo impide. En general, una CSP bloquea todos los scripts que se encuentran directamente en el código (inline scripts). Con ello se protege tanto la página web como a los usuarios de Internet y sobre todo sus datos sensibles. 

La manipulación mediante el Cross Site Scripting es para los ciberdelincuentes algo muy sencillo de realizar. Casi todas las páginas de la World Wide Web tienen un campo de entrada de datos, como pueden ser los comentarios, la barra de búsqueda o los campos de inicio de sesión. En ellos, en lugar de textos, también pueden insertarse scripts. Si el servidor no está protegido adecuadamente, los criminales utilizan interfaces de phishing, inmovilizan la página en su totalidad u obtienen el control del navegador web del usuario mediante software malicioso. La política de seguridad de contenido o, para ser más exactos, su campo en la cabecera, informa al navegador web sobre las fuentes desde las que puede cargar datos. Si se implementa esta política en el código de la página web, se responde al intento de acceder al código infiltrado mediante XSS con un mensaje de error.

Con la Content Security Policy los webmasters también pueden hacer otras configuraciones, por ejemplo, a través de las siguientes directivas:

  • base-uri: limita los URL que pueden aparecer en el elemento <base> de la página web.  
  • child-src: establece las fuentes desde las que pueden aparecer datos en frames, como, por ejemplo, en los vídeos insertados por terceros.
  • connect-src: limita las fuentes con las que se puede vincular una página web, p. ej., a través de enlaces.
  • font-src: determina las fuentes desde las que se pueden cargar los tipos de letra.
  • form-action: facilita una lista de puntos finales válidos en formularios. 
  • frame-ancestors: establece los dominios que pueden incorporar la página web como frames y iFrames.
  • img-src: restringe las fuentes desde las que se pueden cargar imágenes.
  • media-src: establece las fuentes a partir de las que se pueden cargar los formatos de audio y vídeo.
  • object-src: define los controles sobre Flash y otros plugins.
  • plugin-types: limita los tipos de plugins.
  • report-uri: especifica un URL al que se pueden enviar informes si se violan las medidas de seguridad.
  • script-src: define las fuentes permitidas para JavaScript.
  • style-src: funciona como script-src, pero se aplica a las hojas de estilo.
  • upgrade-insecure-requests: establece que las páginas inseguras deben tratarse con HTTP y HTTPS.
  • sandbox: desplaza la página correspondiente a un sandbox (entorno de pruebas), en el que, se prohíben, entre otros, formularios, ventanas emergentes y scripts.

Estas directivas solo se aplican si así se establece expresamente. En caso contrario permanecen abiertas y representan una brecha de seguridad, algo que se puede cambiar con default-src, que establece el estado por defecto de todas las directivas que terminan con -src. En lugar de dejarlas abiertas, podrías regular que solo se carguen datos de la propia página web, a menos que hayas definido una única página de otra forma en la cabecera HTTP. En algunas directivas especiales puedes agregar otras fuentes.

En el campo header pueden insertarse muchas directivas. Si quieres incorporar varias, puedes separarlas con puntos y comas. Además, como webmaster debes especificar todas las fuentes dentro de una directiva, pero no se permiten entradas múltiples de las mismas directivas con fuentes adicionales como en el ejemplo siguiente:

script-src ejemplo1.local; script-src ejemplo2.local

En este solo es relevante la primera fuente, con lo que el cliente ignora la segunda. Por el contrario, se deben anotar ambas fuentes en una directiva:

script-src ejemplo1.local ejemplo2.local

En caso de no necesitar algunos contenidos para una única página o para todo un sitio web, puedes introducir el valor 'none' en la cabecera de las directivas correspondientes. Así se indica que no se puede cargar ninguna fuente. Asimismo, también es posible utilizar el valor 'self' para indicar que el navegador solo debe recargar contenidos de la misma fuente. Ambos valores deben ir siempre entre comillas simples, pues de lo contrario none y self se interpretarán como dominios.

Para definir una política de seguridad de contenido hay diversas posibilidades de cabecera:

  • Content Security Policy
  • X-Webkit-CSP
  • X-Content Security Policy

No todos los navegadores son compatibles con cada denominación. El W3C (el gremio que define los estándares en la Web) recomienda la Content Security Policy, por lo que todos los navegadores modernos se han adaptado a este estándar de seguridad y las otras versiones están obsoletas. Para asegurarte de que llegas con tu CSP al mayor número posible de usuarios de Internet (incluso a los que tienen versiones de navegador anticuadas), es aconsejable integrar todos los campos de la cabecera. Si un navegador web no puede hacer nada con la cabecera de la Content Security Policy, la ignorará y mostrará la página web sin problemas, pero sin mostrarle la protección adicional a los usuarios en cuestión. 

Normalmente, las cabeceras HTTP se definen para todas las páginas del dominio. En el caso de los subdirectorios puede utilizarse el archivo .htaccess. Los estándares de seguridad de CSP ayudan a establecer reglas especiales para cada una de las subpáginas. Si, por ejemplo, quieres implementar un botón para redes sociales en una página, pero no para la siguiente, solo es recomendable permitir el acceso a la fuente externa en la primera página.

Las fuentes pueden introducirse como direcciones, ya sea en su forma propia o como wildcards (comodines). Con respecto a ello, se permiten las siguientes entradas:

  • script-src example.com – Solo se permiten scripts de este dominio por HTTPS.
  • script-src 'none' – No se pueden cargar scripts.
  • script-src 'self' – Solo pueden cargarse scripts que tengan el mismo origen que la página actual, pero no el de los subdominios.
  • script-src https: – Se pueden cargar scripts de cualquier dominio siempre que empiecen por HTTPS.
  • script-src example.com – Se pueden cargar scripts de este dominio.
  • script-src *.example.com – Se permiten scripts de este y de todos los dominios.
  • img-src data: – Pueden cargarse imágenes por medio de enlaces de datos.  

Fundamentalmente, una Content Security Policy establece que solo pueden cargarse scripts a partir de archivos y no de forma directa desde el código de la página web. Para evitarlo, puedes recurrir al comando script-src 'unsafe-inline', pero siendo consciente de que con ello puedes estar creando una brecha de seguridad. Asimismo, el estándar de seguridad también prohíbe la función eval (), comando con el que es posible transformar texto en código JavaScript, algo que también plantea un riesgo para la seguridad. Si todavía se necesita esta función, puede volverse a activar con script-src 'unsafe-eval'.

Consejo

Dando un rodeo se puede asegurar unsafe-inline. Por medio de valores hash o de una fuente nonce pueden cerrarse las brechas de seguridad en gran medida.

Si ya no se permite que los scripts aparezcan directamente en el código, se debe crear un archivo propio para cada uno de ellos. La función del script se externaliza en un archivo .js y el código de la página web solo hace referencia a este: 

<script src='ejemplo.js'></script>

Lo que hace finalmente el script está recogido en el archivo ejemplo.js. Asimismo, los elementos de estilo deben externalizarse en hojas de estilo separadas. Si como webmaster quieres adoptar una política de seguridad en Internet, no solo basta con insertar la cabecera, sino que también es necesario revisar y adaptar el código fuente.

Nota

¿Quieres conocer el nivel de seguridad de tu página? Para ello Mozilla te proporciona un análisis muy útil: el proyecto Observatory by Mozilla asigna una nota al final del análisis y te informa de los aspectos que necesitan una mayor seguridad.

Un ejemplo de Content Security Policy

Con este ejemplo te mostramos cómo crear una cabecera para la política de seguridad de contenido y te explicamos qué se puede conseguir con ella:

Content-Security-Policy: "default-src 'none'; script-src 'self' *.example.com; style-src 'self'; img-src 'self' data:; font-src 'self' fonts.google.com; report-uri example.org/report.html"

X-Content-Security-Policy: "default-src 'none'; script-src 'self' *.example.com; style-src 'self'; img-src 'self' data:; font-src 'self' fonts.google.com; report-uri example.org/report.html"

X-WebKit-CSP: "default-src 'none'; script-src 'self' *.example.com; style-src 'self'; img-src 'self' data:; font-src 'self' fonts.google.com; report-uri example.org"

En el ejemplo se observa que se ha añadido cada variante de CSP a la cabecera para poder abarcar al mayor número de navegadores posible. Dentro de los respectivos nombres de cabecera, el contenido es idéntico: las fuentes se van nombrando de manera sucesiva, las directivas se separan con un punto y coma y la sintaxis es siempre la misma. En realidad, solo varía el nombre del campo, por lo que el contenido puede duplicarse.

En primer lugar se establece que, a no ser que se haya definido de otra manera en una directiva, no se deben cargar datos de ninguna fuente (default-src). Con ello se cierra una brecha de seguridad. Siempre es conveniente definir primero default-src para así evitar que una directiva olvidada provoque una brecha en tu Content Security Policy.  

El paso siguiente es definir la fuente desde la que se tienen que cargar los scripts (script-src). En el ejemplo se ha establecido que el navegador solo cargue scripts de la misma fuente y de example.com, incluidos todos los subdominios (la wildcard se adjudica mediante *). Además, también se indica que los clientes solo tienen permiso para cargar hojas de estilo desde la propia fuente (style-src) y, asimismo, también se permiten imágenes, pero solo desde la propia fuente y como URL de datos (img-src). Según nuestra cabecera de Content Security Policy, solo pueden cargarse tipos de letra del propio origen así como procedentes de la oferta de Google. Por último, en el ejemplo se ha indicado un lugar al que se deben enviar notificaciones en caso de que alguien intente incumplir el estándar de seguridad (report-uri).

Sin embargo, la cabecera no incluye todas las directivas, lo que no supone un problema, pues en el ejemplo adoptado no se necesitan otras whitelists y todas las fuentes se eliminan mediante default-src.

Consejo

En Internet existen numerosos generadores de Content Security Policy. Con opciones como CSP Is Awesome o Report URI [Página de Report URI] (https://report-uri.io/home/generate) puedes crear cabeceras CSP de forma fácil y eficaz.