Política de Seguridad del Contenido

La Política de seguridad de contenido puede reducir de forma significativa el riesgo y el impacto de los ataques de secuencias de comandos en diferentes sitios en los navegadores modernos.

Navegadores compatibles

  • Chrome: 25.
  • Edge: 14.
  • Firefox: 23.
  • Safari: 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 https://evil.example.com nunca debe tener acceso. En teoría, cada origen se mantiene aislado del resto de la Web, lo que les brinda a los desarrolladores una zona de pruebas segura en la que compilar. Sin embargo, en la práctica, los atacantes encontraron varias formas de subvertir el sistema.

Los ataques de secuencia de comandos entre sitios (XSS), por ejemplo, omiten la política del mismo origen engañando a 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 engañar esa confianza al insertar código malicioso. Si un atacante logra insertar cualquier código, habrá comprometido la sesión del usuario y obtenido 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 XSS en navegadores modernos.

Componentes de CSP

Para implementar un CSP eficaz, sigue estos pasos:

  • Usa listas de entidades permitidas para comunicarle al cliente qué se permite y qué no.
  • Obtén información sobre las directivas disponibles.
  • Aprende las palabras clave que llevan.
  • Restringe el uso de código intercalado y eval().
  • Denuncia incumplimientos de la política de tus servidores antes de implementarlas.

Listas de entidades permitidas de la fuente

Los ataques XSS aprovechan la incapacidad del navegador para distinguir entre una secuencia de comandos que es parte de tu aplicación y una secuencia de comandos insertada maliciosamente por un tercero. Por ejemplo, el botón de +1 de Google en la parte inferior de esta página carga y ejecuta 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 sí mismo que el código de apis.google.com es seguro para ejecutar, 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 renderice solo recursos de esas fuentes. Incluso si un atacante puede encontrar un hueco 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 entrega código válido y confiamos en que nosotros también lo hacemos. Este es un ejemplo de política 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 las 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 de apis.google.com a través de HTTPS, como también del origen de la página actual, pero no de ningún otro origen. Si un atacante inserta código en tu sitio, el navegador muestra un error y no ejecuta la secuencia de comandos insertada.

Error de consola: No se cargó la secuencia de comandos "http://evil.example.com/evil.js" porque incumple la siguiente directiva de la Política de seguridad de contenido: script-src "self" https://apis.google.com
La consola 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 una página puede cargar, 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 redactó una especificación de nivel 3, pero no se implementó en gran medida en los navegadores principales.

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 los marcos incorporados. Por ejemplo, child-src https://youtube.com habilita la incorporación de videos de YouTube, pero no de otros orígenes.
connect-src
Limita los orígenes a los que puedes conectarte con XHR, WebSockets y EventSource.
font-src
Especifica los orígenes que pueden entregar fuentes web. Por ejemplo, puedes permitir las fuentes web de Google con font-src https://themes.googleusercontent.com.
form-action
Enumera los 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 restableció en el nivel 3. Si no está presente, el navegador vuelve a child-src.
img-src
Define los orígenes desde los que se pueden cargar las imágenes.
media-src
Restringe los orígenes permitidos para entregar 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 de contenido. Esta directiva no se puede usar en etiquetas <meta>.
style-src
Limita los orígenes de los que una página puede usar hojas de estilo.
upgrade-insecure-requests
Indica a los usuario-agentes que modifiquen los esquemas de URL cambiando HTTP por HTTPS. Esta directiva es para sitios web con grandes cantidades de URLs antiguas que necesitan modificarse.
worker-src
Una directiva de nivel 3 de CSP que restringe las URLs que se pueden cargar como trabajador, trabajador compartido o trabajador de servicio. A partir de 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 la configuración predeterminada, especifica una directiva default-src. Esta directiva define los valores predeterminados para cualquier directiva no especificada que finalice con -src. Por ejemplo, si configuras default-src en https://example.com y no especificas una directiva font-src, puedes cargar fuentes solo desde https://example.com.

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

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

Sintaxis básica de CSP

Para usar directivas de CSP, inclúyelas 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 única 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 contenido en https://cdn.example.net y no usa complementos ni contenido dentro de marcos:

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

Detalles de 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 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 particulares. Por ejemplo, si un conjunto de páginas de tu sitio tiene un botón de +1, mientras que otras 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 las fuentes por esquema (data:, https:) o variar en especificidad desde solo el nombre de host (example.com, que coincide con cualquier origen en ese host: cualquier esquema, cualquier puerto) hasta un URI completamente calificado (https://example.com:443, que solo coincide con HTTPS, solo example.com y solo el puerto 443). Se aceptan los comodines, pero únicamente como un esquema, un puerto o en la posición de más a la izquierda del nombre de host: *://*.example.com:* coincidiría con todos los subdominios de example.com (pero no con el propio example.com), que usen 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), lo cual probablemente no sea tu intención.

Prueba tus páginas en la zona de pruebas

Hay una directiva más que vale la pena mencionar: sandbox. Es un poco diferente de otras que hemos visto, ya que restringe acciones que la página puede realizar en vez de recursos que la página puede cargar. Si se encuentra la directiva sandbox, la página se trata como si se cargara dentro de un <iframe> con un atributo sandbox. Esto puede producir una variedad de efectos en la página: forzar la página a un único origen y evitar el envío de formularios, entre otros. Esto se sale un poco del tema de esta página, pero puedes encontrar detalles completos sobre los atributos de zona de pruebas válidos en la sección "Zona de pruebas" de la especificación de HTML5.

La metaetiqueta

El mecanismo de entrega preferido de la CSP es un encabezado HTTP. Sin embargo, puede resultar útil establecer una política para una página directamente en el lenguaje de marcado. Hazlo con 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 o sandbox.

Evita el código intercalado

Por más potentes que sean las listas de entidades permitidas basadas en el origen que se usan en las directivas del CSP, no pueden resolver la mayor amenaza que representan los ataques XSS: la inserción de secuencias de comandos intercaladas. Si un atacante puede insertar una etiqueta de secuencia de comandos que contiene directamente una carga útil maliciosa (como <script>sendMyDataToEvilDotCom()</script>), el navegador no tiene forma de distinguirla de una etiqueta de secuencia de comandos intercalada legítima. La CSP resuelve este problema al rechazar completamente las secuencias de comandos intercaladas.

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

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

a algo como lo siguiente:

<!-- 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 la CSP, sino que también está alineado con las prácticas recomendadas de diseño web. El código JavaScript intercalado mezcla la estructura y el comportamiento de manera que el código resulta confuso. También es más complicado almacenar en caché y compilar. Mover el código a recursos externos mejora el rendimiento de tus páginas.

También se recomienda mover las etiquetas y los 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 secuencias de comandos y estilos intercalados

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

Para usar un nonce, dale a tu etiqueta de secuencia de comandos un atributo nonce. Su valor debe coincidir con uno de la lista de fuentes de contenido confiables. 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 tu directiva script-src después de la palabra clave nonce-:

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

Los nonces deben regenerarse 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 propia secuencia de comandos y agrégala a la directiva script-src. Por ejemplo, si tu página contenía lo siguiente:

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

Tu 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 CSP también admite sha384- y sha512-. Cuando generes el hash, omíte las etiquetas <script>. El uso de mayúsculas y espacios en blanco es importante, inclusive los espacios en blanco al principio y al final.

Las soluciones para generar hashes SHA están disponibles en cualquier idioma. Con Chrome 40 o versiones posteriores, puedes abrir DevTools y luego volver a cargar tu página. La pestaña Console muestra 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 puede insertar una secuencia de comandos directamente, es posible que pueda 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 todos vectores que los atacantes pueden usar para ejecutar código malicioso a través de texto insertado. La respuesta predeterminada de la CSP a este riesgo es bloquear completamente todos estos vectores.

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

  • Debes analizar JSON con el JSON.parse incorporado, en lugar de depender de eval. Las operaciones seguras de JSON están disponibles en todos los navegadores a partir de IE8.
  • Debes volver a escribir cualquier llamada a setTimeout o setInterval que realices con funciones intercaladas en lugar de cadenas. Por ejemplo, si tu página contiene lo siguiente:

    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() con frecuencia 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 forma predeterminada, por lo que tienen que retroceder a un analizador robusto ante la ausencia de eval. La directiva ng-csp de AngularJS es un buen ejemplo de esto. Sin embargo, te recomendamos que uses un lenguaje de plantillas que ofrezca precompilación, como Handlebars. Precompilar tus plantillas puede acelerar la experiencia del usuario incluso más que la implementación más rápida en tiempo de ejecución, 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 una fuente permitida en una directiva script-src. No recomendamos esta opción debido al riesgo de inserción de código que presenta.

Denuncia incumplimientos de política

Para notificar al servidor sobre errores que podrían permitir una inserción maliciosa, puedes decirle al navegador que envíe POST informes de incumplimiento con formato JSON a 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 de un incumplimiento de política, incluida la página en la que ocurrió (document-uri), el referrer de esa página, el recurso que incumplió la política de la página (blocked-uri), la directiva específica que incumplió (violated-directive) y la política completa de la página (original-policy).

Solo informes

Si estás empezando a usar CSP, te recomendamos que uses el modo solo de informes para evaluar el estado de tu 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 informativo no bloquea los recursos restringidos, pero sí envía informes de incumplimiento a la ubicación que especifiques. Incluso puedes enviar ambos encabezados para aplicar una política mientras se supervisa otra. Esta es una forma excelente de probar los cambios en tu CSP mientras aplicas la política actual: activa los informes para una nueva política, supervisa los informes de incumplimiento y corrige los errores, y cuando estés conforme con la nueva política, comienza a aplicarla.

Uso en el mundo 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 analizan algunos casos de uso comunes y el proceso de decisión para admitirlos de acuerdo con los lineamientos del CSP.

Widgets de redes sociales

  • El botón Me gusta de Facebook tiene varias opciones de implementación. Te recomendamos que uses la versión <iframe> para mantenerla aislada del resto de tu sitio. Necesita una directiva child-src https://facebook.com para funcionar correctamente.
  • El botón Twittear de X depende del acceso a una secuencia de comandos. Mueve la secuencia de comandos que proporciona a un archivo JavaScript externo y usa la directiva script-src https://platform.twitter.com; child-src https://platform.twitter.com.
  • Otras plataformas tienen requisitos similares, y puedes abordarlos de manera similar. Para probar estos recursos, te recomendamos que configures un default-src de 'none' y que revises tu consola para determinar qué recursos 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

Bloqueo

En el caso de algunos sitios web, te recomendamos que te asegures de que solo se puedan cargar recursos locales. En el siguiente ejemplo, se desarrolla una CSP para un sitio bancario, comenzando con una política predeterminada que bloquea todo (default-src 'none').

El sitio carga todas las imágenes, estilos y secuencias de comandos desde una CDN en https://cdn.mybank.net y se conecta a https://api.mybank.com/ con XHR para recuperar datos. Usa marcos, pero solo para páginas locales del sitio (no orígenes de terceros). En el sitio no hay Flash, fuentes ni elementos adicionales. 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 un administrador de un foro que quiere asegurarse de que todos los recursos de su foro solo se carguen a través de canales seguros, pero no tiene experiencia en codificación y no tiene los recursos para reescribir el software de foros 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 se especifica https: en default-src, las directivas de secuencia de comandos y estilo no heredan automáticamente esa fuente. Cada directiva anula el valor predeterminado para ese tipo específico de recurso.

Desarrollo de estándares de CSP

La Política de seguridad de contenido nivel 2 es un estándar recomendado del W3C. El Grupo de trabajo de seguridad en aplicaciones web del W3C está desarrollando la siguiente iteración de la especificación, Política de seguridad de contenido nivel 3.

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