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.
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 fuentes
Los ataques de XSS explotan la incapacidad del navegador para distinguir entre la secuencia de comandos que forma parte de tu aplicación y la secuencia de comandos insertada de forma malintencionada por un tercero. Por ejemplo, el botón de +1 de Google al final 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. 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 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.
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 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 restablecerá en el nivel 3. Si no está presente, el navegador recurre a
child-src
. img-src
- Define los orígenes desde los que se pueden cargar las imágenes.
media-src
- Restringe los orígenes autorizados 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 termina en -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 resguardo. Recuerda que no establecerlas es lo mismo que permitir cualquier cosa:
base-uri
form-action
frame-ancestors
plugin-types
report-uri
sandbox
Sintaxis básica de la 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 específicas. 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 fuentes por esquema (data:
, https:
), o en un rango de especificidad desde solo el nombre de host (example.com
, que coincide con cualquier origen en ese host: cualquier esquema, cualquier puerto) a 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 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 comoeval
. Para obtener más información, consulta Evita usareval()
.
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 ser útil establecer una política en 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
ni 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 bloqueo incluye no solo las secuencias de comandos incorporadas directamente en las etiquetas script
, sino también
los controladores de eventos intercalados y las URLs de javascript:
. Deberás mover el contenido de
las etiquetas script
a un archivo externo y reemplazar las URLs javascript:
y <a ...
onclick="[JAVASCRIPT]">
con las llamadas addEventListener()
correspondientes. 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 tu 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 estilos y secuencias de comandos intercalados
Para habilitar secuencias de comandos y 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, asigna a tu etiqueta de secuencia de comandos un atributo nonce. 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 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, omite las etiquetas <script>
. mayúsculas y asuntos en blanco, incluidos los espacios en blanco al inicio y al final.
Las soluciones para generar hashes de 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
integrado, en lugar de depender deeval
. Las operaciones JSON seguras están disponibles en todos los navegadores a partir de IE8. Debes volver a escribir cualquier llamada a
setTimeout
osetInterval
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 inmediato y recurre a un analizador sólido ante la ausencia deeval
. 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 excelente manera de probar los cambios en tu CSP mientras aplicas tu política actual: activar los informes para una política nueva, supervisar los informes de incumplimiento y corregir cualquier error. 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 explican algunos casos de uso comunes y el proceso de decisión para respaldarlos según los lineamientos de los 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 mantenerla en una zona de pruebas del resto de tu sitio. Necesita una directivachild-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 foro externo 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 estándar 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@.