Política de Seguridad del Contenido

La Política de Seguridad del Contenido puede reducir significativamente el riesgo y el impacto de los ataques de secuencia de comandos entre sitios en los navegadores modernos.

Navegadores compatibles

  • 25
  • 14
  • 23
  • 7

Origen

El modelo de seguridad de la Web se basa en una política del mismo origen. Por ejemplo, el código de https://mybank.com debe tener acceso solo a los datos de https://mybank.com y nunca debe permitirse el acceso a https://evil.example.com. En teoría, cada origen se mantiene aislado del resto de la Web, lo que brinda a los desarrolladores una zona de pruebas segura para compilar. Sin embargo, en la práctica, los atacantes han encontrado varias formas de subvertir el sistema.

Los ataques de secuencia de comandos entre sitios (XSS), por ejemplo, omiten la política del mismo origen mediante el engaño de un sitio para que entregue código malicioso junto con el contenido previsto. Este es un gran problema, ya que los navegadores confían en todo el código que aparece en una página como parte legítima del origen de seguridad de esa página. La hoja de referencia de XSS es una muestra antigua pero representativa de los métodos que un atacante podría usar para infringir esta confianza mediante la inyección de código malicioso. Si un atacante inyecta con éxito cualquier código, comprometió la sesión del usuario y obtuvo acceso a información privada.

En esta página, se describe la Política de Seguridad del Contenido (CSP) como una estrategia para reducir el riesgo y el impacto de los ataques de XSS en los navegadores modernos.

Componentes de la CSP

Para implementar una CSP eficaz, sigue estos pasos:

  • Usa listas de entidades permitidas para indicarle al cliente lo que está permitido y lo que no.
  • Obtén información sobre las directivas disponibles.
  • Conozca las palabras clave con las que cuenta.
  • Restringe el uso de código intercalado y eval().
  • Denuncia incumplimientos de política en tu servidor antes de aplicarlos.

Listas de fuentes permitidas

Los ataques de XSS se aprovechan de la incapacidad del navegador para distinguir entre una secuencia de comandos que es parte de tu aplicación y una secuencia de comandos que un tercero inyectó de forma malintencionada. Por ejemplo, el botón de +1 de Google en la parte inferior de esta página carga y ejecuta el código de https://apis.google.com/js/plusone.js en el contexto del origen de esta página. Confiamos en ese código, pero no podemos esperar que el navegador determine por su cuenta que es seguro ejecutar el código de apis.google.com, mientras que el código de apis.evil.example.com probablemente no lo sea. El navegador descarga y ejecuta con gusto cualquier código que una página solicite, sin importar la fuente.

El encabezado HTTP Content-Security-Policy de la CSP te permite crear una lista de entidades permitidas de fuentes de contenido confiable y le indica al navegador que ejecute o procese solo los recursos de esas fuentes. Incluso si un atacante puede encontrar un agujero para insertar una secuencia de comandos, esta no coincidirá con la lista de entidades permitidas y, por lo tanto, no se ejecutará.

Confiamos en que apis.google.com proporcionará código válido y a nosotros mismos. Esta es una política de ejemplo que permite que las secuencias de comandos se ejecuten solo cuando provienen de una de esas dos fuentes:

Content-Security-Policy: script-src 'self' https://apis.google.com

script-src es una directiva que controla un conjunto de privilegios relacionados con secuencias de comandos para una página. Este encabezado 'self' como una fuente válida de secuencia de comandos y https://apis.google.com como otra. El navegador ahora puede descargar y ejecutar JavaScript desde apis.google.com a través de HTTPS, además del origen de la página actual, pero no desde ningún otro origen. Si un atacante inyecta código en tu sitio, el navegador muestra un error y no ejecuta la secuencia de comandos inyectada.

Error de la consola: Se rechazó la carga de la secuencia de comandos "http://evil.example.com/evil.js" porque infringe la siguiente directiva de la Política de Seguridad del Contenido: script-src "self" https://apis.google.com
En la consola, se muestra un error cuando una secuencia de comandos intenta ejecutarse desde un origen que no está en la lista de entidades permitidas.

La política se aplica a una amplia variedad de recursos

La CSP proporciona un conjunto de directivas de políticas que permiten un control detallado sobre los recursos que puede cargar una página, incluido script-src del ejemplo anterior.

En la siguiente lista, se describe el resto de las directivas de recursos a partir del nivel 2. Se creó una especificación de nivel 3, pero no está implementada en los navegadores más importantes.

base-uri
Restringe las URLs que pueden aparecer en el elemento <base> de una página.
child-src
Enumera las URLs para los trabajadores y el contenido de marcos incorporados. Por ejemplo, child-src https://youtube.com permite incorporar videos de YouTube, pero no de otros orígenes.
connect-src
Limita los orígenes a los que puedes conectarte mediante XHR, WebSockets y EventSource.
font-src
Especifica los orígenes que pueden entregar fuentes web. Por ejemplo, puedes permitir las fuentes web de Google usando font-src https://themes.googleusercontent.com.
form-action
Enumera extremos válidos para el envío desde etiquetas <form>.
frame-ancestors
Especifica las fuentes que pueden incorporar la página actual. Esta directiva se aplica a las etiquetas <frame>, <iframe>, <embed> y <applet>. No se puede usar en etiquetas <meta> ni para recursos HTML.
frame-src
Esta directiva dejó de estar disponible en el nivel 2, pero se restablece en el nivel 3. Si no está presente, el navegador recurre a child-src.
img-src
Define desde qué orígenes se pueden cargar las imágenes.
media-src
Restringe los orígenes permitidos para enviar video y audio.
object-src
Permite controlar Flash y otros complementos.
plugin-types
Limita los tipos de complementos que puede invocar una página.
report-uri
Especifica una URL a la que el navegador envía informes cuando se incumple una política de seguridad del contenido. No se puede usar esta directiva en las etiquetas <meta>.
style-src
Limita los orígenes desde los que una página puede usar hojas de estilo.
upgrade-insecure-requests
Indica a los usuarios-agentes que cambien de HTTP a HTTPS para reescribir los esquemas de URL. Esta directiva es para sitios web con una gran cantidad de URLs antiguas que deben reescribirse.
worker-src
Una directiva de CSP nivel 3 que restringe las URLs que se pueden cargar como trabajador, trabajador compartido o service worker. Desde julio de 2017, esta directiva tiene implementaciones limitadas.

De forma predeterminada, el navegador carga el recurso asociado desde cualquier origen, sin restricciones, a menos que establezcas una política con una directiva específica. Para anular el valor predeterminado, especifica una directiva default-src. Esta directiva define los valores predeterminados de cualquier directiva no especificada que termine en -src. Por ejemplo, si configuras default-src en https://example.com y no especificas una directiva font-src, solo podrás cargar fuentes desde https://example.com.

Las siguientes directivas no usan default-src como resguardo. Recuerda que no establecerlas equivale a permitir todo:

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

Sintaxis básica de la CSP

Para usar directivas de la CSP, enuméralas en el encabezado HTTP con directivas separadas por dos puntos. Asegúrate de enumerar todos los recursos necesarios de un tipo específico en una sola directiva de la siguiente manera:

script-src https://host1.com https://host2.com

El siguiente es un ejemplo de varias directivas, en este caso para una app web que carga todos sus recursos desde una red de distribución de contenidos en https://cdn.example.net y no usa complementos ni contenido enmarcado:

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

Detalles de la implementación

Los navegadores modernos admiten el encabezado Content-Security-Policy sin prefijo. Este es el encabezado recomendado. Los encabezados X-WebKit-CSP y X-Content-Security-Policy que puedes ver en los instructivos en línea dejaron de estar disponibles.

La CSP se define página por página. Deberás enviar el encabezado HTTP con cada respuesta que quieras proteger. Esto te permite ajustar la política para páginas específicas según sus necesidades específicas. Por ejemplo, si un conjunto de páginas de tu sitio tiene el botón de +1 y otros no, puedes permitir que el código del botón se cargue solo cuando sea necesario.

La lista de fuentes de contenido para cada directiva es flexible. Puedes especificar fuentes por esquema (data:, https:) o en especificidad desde solo el nombre de host (example.com, que coincide con cualquier origen en ese host: cualquier esquema o cualquier puerto) hasta un URI completamente calificado (https://example.com:443, que coincide solo con HTTPS, solo con example.com y solo el puerto 443). Se aceptan comodines, pero solo como un esquema, un puerto o en la posición más a la izquierda del nombre de host: *://*.example.com:* coincidiría con todos los subdominios de example.com (pero no el propio example.com), con cualquier esquema, en cualquier puerto.

La lista de fuentes de contenido también acepta cuatro palabras clave:

  • 'none' no coincide con nada.
  • 'self' coincide con el origen actual, pero no con sus subdominios.
  • 'unsafe-inline' permite JavaScript y CSS intercalados. Para obtener más información, consulta Cómo evitar el código intercalado.
  • 'unsafe-eval' permite mecanismos de texto a JavaScript, como eval. Para obtener más información, consulta Cómo evitar eval().

Estas palabras clave requieren comillas simples. Por ejemplo, script-src 'self' (con comillas) autoriza la ejecución de JavaScript desde el host actual; script-src self (sin comillas) permite JavaScript desde un servidor llamado "self" (y no desde el host actual), que probablemente no sea lo que querías.

Crea una zona de pruebas para tus páginas

Hay una directiva más sobre la que vale la pena mencionar: sandbox. Es un poco diferente de las otras que hemos visto, ya que impone restricciones sobre las acciones que puede realizar la página en lugar de los recursos que puede cargar. Si la directiva sandbox está presente, la página se trata como si se hubiera cargado dentro de un objeto <iframe> con un atributo sandbox. Esto puede tener una gran variedad de efectos en la página, como forzar a la página a un origen único y evitar el envío de formularios, entre otros. No se incluye en esta página, pero puedes encontrar todos los detalles sobre los atributos válidos de la zona de pruebas en la sección "Zona de pruebas" de la especificación HTML5.

La metaetiqueta

El mecanismo de entrega preferido de la CSP es un encabezado HTTP. Sin embargo, puede ser útil establecer una política en una página directamente en el lenguaje de marcado. Para ello, usa una etiqueta <meta> con un atributo http-equiv:

<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">

No se puede usar para frame-ancestors, report-uri ni sandbox.

Evita el código intercalado

Tan potentes como las listas de entidades permitidas basadas en el origen que se usan en las directivas de la CSP, no pueden resolver la mayor amenaza que representan los ataques de XSS: la inyección de secuencias de comandos intercaladas. Si un atacante puede insertar una etiqueta de secuencia de comandos que contiene directamente alguna carga útil maliciosa (como <script>sendMyDataToEvilDotCom()</script>), el navegador no tiene manera de distinguirla de una etiqueta legítima de secuencia de comandos intercalada. La CSP resuelve este problema bloqueando por completo las secuencias de comandos intercaladas.

Este bloqueo no solo incluye secuencias de comandos incorporadas directamente en las etiquetas script, sino también controladores de eventos intercalados y URLs javascript:. Deberás mover el contenido de las etiquetas script a un archivo externo y reemplazar las URLs javascript: y <a ... onclick="[JAVASCRIPT]"> por las llamadas addEventListener() correspondientes. Por ejemplo, puedes reescribir lo siguiente:

<script>
    function doAmazingThings() {
    alert('YOU ARE AMAZING!');
    }
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>

por algo como esto:

<!-- amazing.html -->
<script src='amazing.js'></script>
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
    alert('YOU ARE AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('amazing')
    .addEventListener('click', doAmazingThings);
});

El código reescrito no solo es compatible con CSP, sino que también se alinea con las prácticas recomendadas de diseño web. JavaScript intercalado mezcla la estructura y el comportamiento de maneras que hacen que el código sea confuso. También es más complicado almacenar en caché y compilar. Mover el código a recursos externos hace que tus páginas tengan un mejor rendimiento.

También se recomienda mover etiquetas y atributos style intercalados a hojas de estilo externas para proteger tu sitio contra ataques de robo de datos basados en CSS.

Cómo permitir temporalmente estilos y secuencias de comandos intercalados

Para habilitar estilos y secuencias de comandos intercalados, agrega 'unsafe-inline' como fuente permitida en una directiva script-src o style-src. El nivel 2 de la CSP también te permite agregar secuencias de comandos intercaladas específicas a la lista de entidades permitidas con un nonce criptográfico (un número que se usa una vez) o un hash de la siguiente manera.

Para usar un nonce, asígnale un atributo nonce a la etiqueta de la secuencia de comandos. Su valor debe coincidir con uno de la lista de fuentes de confianza. Por ejemplo:

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
    // Some inline code I can't remove yet, but need to as soon as possible.
</script>

Agrega el nonce a la directiva script-src después de la palabra clave nonce-:

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

Los nonces deben volver a generarse para cada solicitud de página y deben ser indescifrables.

Los hash funcionan de manera similar. En lugar de agregar código a la etiqueta de la secuencia de comandos, crea un hash SHA de la secuencia de comandos y agrégalo a la directiva script-src. Por ejemplo, si tu página incluye lo siguiente:

<script>alert('Hello, world.');</script>

La política debe contener lo siguiente:

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

El prefijo sha*- especifica el algoritmo que genera el hash. En el ejemplo anterior, se usa sha256-, pero la CSP también es compatible con sha384- y sha512-. Cuando generes el hash, omite las etiquetas <script>. Las mayúsculas y la cuestión de los espacios en blanco, incluidos los espacios en blanco al inicio y al final.

Las soluciones para generar hashes de SHA están disponibles en cualquier cantidad de lenguajes. Con Chrome 40 o versiones posteriores, puedes abrir Herramientas para desarrolladores y, luego, volver a cargar la página. En la pestaña Consola, se muestran mensajes de error con el hash SHA-256 correcto para cada una de tus secuencias de comandos intercaladas.

Evita eval()

Incluso cuando un atacante no pueda insertar una secuencia de comandos de forma directa, podría engañar a tu aplicación para que convierta el texto de entrada en JavaScript ejecutable y lo ejecute en su nombre. eval(), new Function(), setTimeout([string], …) y setInterval([string], ...) son vectores que los atacantes pueden usar para ejecutar código malicioso a través de texto inyectado. La respuesta predeterminada de la CSP a este riesgo es bloquear todos estos vectores por completo.

Esto tiene los siguientes efectos en la forma en que compilas aplicaciones:

  • Debes analizar JSON con el JSON.parse integrado, en lugar de depender de eval. Las operaciones seguras de JSON están disponibles en todos los navegadores desde IE8.
  • Debes volver a escribir cualquier llamada a setTimeout o setInterval que hagas con funciones intercaladas en lugar de cadenas. Por ejemplo, supongamos que tu página contiene los siguientes elementos:

    setTimeout("document.querySelector('a').style.display = 'none';", 10);
    

    Vuelve a escribirlo de la siguiente manera:

    setTimeout(function () {
        document.querySelector('a').style.display = 'none';
    }, 10);
      ```
    
  • Evita el uso de plantillas intercaladas durante el tiempo de ejecución. Muchas bibliotecas de plantillas usan new Function() a menudo para acelerar la generación de plantillas durante el tiempo de ejecución, lo que permite la evaluación de texto malicioso. Algunos frameworks admiten CSP de inmediato y recurren a un analizador sólido cuando no existe eval. La directiva ng-csp de AngularJS es un buen ejemplo de esto. Sin embargo, te recomendamos que uses un lenguaje de plantilla que ofrezca compilación previa, como Handlebars. La compilación previa de tus plantillas puede hacer que la experiencia del usuario sea aún más rápida que la implementación del entorno de ejecución más rápida, además de hacer que tu sitio sea más seguro.

Si eval() o alguna otra función de texto a JavaScript son esenciales para tu aplicación, puedes habilitarlas agregando 'unsafe-eval' como fuente permitida en una directiva script-src. No recomendamos esta acción debido al riesgo de inserción de código que representa.

Denuncia incumplimientos de política

Para notificar al servidor sobre errores que podrían permitir la inserción maliciosa, puedes indicarle al navegador que POST los informes de incumplimientos con formato JSON en una ubicación especificada en una directiva report-uri:

Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

Estos informes se ven de la siguiente manera:

{
    "csp-report": {
    "document-uri": "http://example.org/page.html",
    "referrer": "http://evil.example.com/",
    "blocked-uri": "http://evil.example.com/evil.js",
    "violated-directive": "script-src 'self' https://apis.google.com",
    "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
    }
}

El informe contiene información útil para encontrar la causa del incumplimiento de políticas, como la página en la que ocurrió (document-uri), el referrer de esa página, el recurso que infringió la política de la página (blocked-uri), la directiva específica que infringió (violated-directive) y la política completa de la página (original-policy).

Solo informes

Si recién comienzas a usar CSP, te recomendamos que uses el modo de solo informes para evaluar el estado de la app antes de cambiar la política. Para ello, en lugar de enviar un encabezado Content-Security-Policy, envía un encabezado Content-Security-Policy-Report-Only:

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

La política especificada en el modo de solo informes no bloquea los recursos restringidos, pero envía informes de incumplimientos a la ubicación que especifiques. Incluso puedes enviar ambos encabezados para aplicar una política mientras se supervisa otra. Esta es una excelente manera de probar los cambios en tu CSP mientras aplicas tu política actual: activa los informes de una política nueva, supervisa los informes de incumplimiento y corrige cualquier error y, cuando estés conforme con la política nueva, comienza a aplicarla.

Uso real

El primer paso para crear una política para tu app es evaluar los recursos que carga. Cuando comprendas la estructura de tu app, crea una política basada en sus requisitos. En las siguientes secciones, se explican algunos casos de uso comunes y el proceso de decisión para respaldarlos mediante los lineamientos de la CSP.

Widgets de redes sociales

  • El botón Me gusta de Facebook tiene varias opciones de implementación. Te recomendamos usar la versión <iframe> para mantener la zona de pruebas del resto de tu sitio. Necesita una directiva child-src https://facebook.com para funcionar correctamente.
  • El botón de tweet de X depende del acceso a una secuencia de comandos. Mueve la secuencia de comandos que proporciona a un archivo de JavaScript externo y usa la directiva script-src https://platform.twitter.com; child-src https://platform.twitter.com.
  • Otras plataformas tienen requisitos similares y se pueden abordar de manera similar. A fin de probar estos recursos, te recomendamos configurar un default-src de 'none' y observar la consola para determinar los recursos que deberás habilitar.

Para usar varios widgets, combina las directivas de la siguiente manera:

script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com

Aislamiento

En algunos sitios web, querrás asegurarte de que solo se puedan cargar los recursos locales. En el siguiente ejemplo, se desarrolla una CSP para el sitio de un banco, a partir de una política predeterminada que bloquea todo (default-src 'none').

El sitio carga todas las imágenes, el estilo y la secuencia de comandos desde una CDN en https://cdn.mybank.net, y se conecta a https://api.mybank.com/ mediante XHR para recuperar los datos. Usa marcos, pero solo para páginas locales del sitio (sin orígenes de terceros). El sitio no tiene Flash, fuentes ni extras. El encabezado de CSP más restrictivo que puede enviar es el siguiente:

Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'

Solo SSL

El siguiente es un ejemplo de CSP para el administrador de un foro que desea asegurarse de que todos los recursos de su foro se carguen solo con canales seguros, pero no tiene experiencia en codificación y no tiene los recursos para reescribir software de foro de terceros lleno de secuencias de comandos y estilos intercalados:

Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'

Aunque https: se especifica en default-src, las directivas de secuencia de comandos y estilo no heredan automáticamente esa fuente. Cada directiva reemplaza el valor predeterminado para ese tipo específico de recurso.

Desarrollo estándar de CSP

La Política de Seguridad del Contenido de nivel 2 es un estándar recomendado de W3C. El grupo de trabajo de seguridad para aplicaciones web de W3C está desarrollando la próxima iteración de la especificación, la Política de Seguridad del Contenido de nivel 3.

Para seguir el debate sobre estas próximas funciones, consulta los archivos de la lista de distribución public-webappsec@.