Mitiga la secuencia de comandos entre sitios (XSS) con una Política de Seguridad del Contenido (CSP) estricta

Navegadores compatibles

  • Chrome: 52.
  • Borde: 79.
  • Firefox: 52.
  • Safari: 15.4.

Origen

Secuencia de comandos entre sitios (XSS), la capacidad de inyectar secuencias de comandos maliciosas en una aplicación web ha sido uno de los las más grandes vulnerabilidades de seguridad web desde hace más de una década.

Política de Seguridad del Contenido (CSP) es una capa adicional de seguridad que ayuda a mitigar el XSS. Para configurar un CSP, agrega el encabezado HTTP Content-Security-Policy a una página web y establece valores controlar qué recursos puede cargar el usuario-agente para esa página.

En esta página, se explica cómo usar un CSP basado en nonces o hashes para mitigar XSS. en lugar de los CSP basados en listas de entidades permitidas de host de uso general que a menudo salen de la página. Se exponen a XSS porque se pueden omitir en la mayoría de las configuraciones.

Término clave: un nonce es un número al azar usado solo una vez y que se puede usar para marcar un La etiqueta <script> es de confianza.

Término clave: una función hash es una función matemática que convierte una entrada de salida en un valor numérico comprimido llamado hash. Puedes usar un hash (por ejemplo, SHA-256) para marcar un La etiqueta <script> es de confianza.

Una Política de Seguridad del Contenido basada en nonces o hashes suele denominarse CSP estricta. Cuando una aplicación usa una CSP estricta, los atacantes que encuentran HTML por lo general, no se pueden usar para forzar la ejecución del navegador de comandos maliciosas en documentos vulnerables. Esto se debe a que una CSP estricta permite secuencias de comandos con codificación hash o secuencias de comandos con el valor correcto de nonce generado en el por lo que los atacantes no pueden ejecutar la secuencia de comandos sin conocer el nonce correcto para una respuesta dada.

¿Por qué deberías usar una CSP estricta?

Si tu sitio ya tiene una CSP similar a script-src www.googleapis.com, lo que probablemente no sea eficaz contra sitios múltiples. Este tipo de CSP se denomina CSP en la lista de entidades permitidas. Requieren mucha personalización y se pueden evitadas por los atacantes.

Las CSP estrictas basadas en nonces o hashes criptográficos evitan estas dificultades.

Estructura estricta de la CSP

Una política de seguridad del contenido estricta básica usa una de las siguientes respuestas HTTP encabezados:

CSP estricta basada en nonce

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
Cómo funciona una CSP estricta basada en nonce.

CSP estricta basada en hash

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Las siguientes propiedades hacen que una CSP como esta sea "estricta" y, por lo tanto, proteger:

  • Usa nonces 'nonce-{RANDOM}' o hashes 'sha256-{HASHED_INLINE_SCRIPT}' para indicar en qué etiquetas <script> confía el desarrollador del sitio. el navegador del usuario.
  • Establece 'strict-dynamic' para reducir el esfuerzo de implementar una CSP basada en nonce o hash lo que permite la ejecución de secuencias de comandos creadas por una secuencia de comandos confiable. Esto también desbloquea el uso de la mayoría de las bibliotecas y widgets de JavaScript de terceros.
  • No se basa en listas de URLs permitidas, por lo que no omisiones de CSP comunes.
  • Bloquea secuencias de comandos intercaladas que no son de confianza, como los controladores de eventos intercalados o javascript: URIs.
  • Restringe object-src para inhabilitar complementos peligrosos, como Flash.
  • Restringe base-uri para bloquear la inserción de etiquetas <base>. Esto evita que los atacantes cambien la ubicación de las secuencias de comandos cargadas desde las URL relativas.

Adopta una CSP estricta

Para adoptar una CSP estricta, debes hacer lo siguiente:

  1. Decide si tu aplicación debe establecer una CSP basada en nonce o hash.
  2. Copia la CSP de la sección Estructura estricta de la CSP y configúrala. como encabezado de respuesta en tu aplicación.
  3. Refactoriza las plantillas HTML y el código del cliente para eliminar los patrones que se incompatibles con CSP.
  4. Implementa tu CSP.

Puedes usar Lighthouse (v7.3.0 y versiones posteriores con la marca --preset=experimental) Auditoría de prácticas recomendadas a lo largo de este proceso para verificar si tu sitio tiene un CSP lo suficientemente estrictos como para ser eficaz contra XSS.

Faro
  un informe con una advertencia que indica que no se encuentra ninguna CSP en el modo de aplicación forzosa.
Si tu sitio no tiene una CSP, Lighthouse muestra esta advertencia.

Paso 1: Decide si necesitas una CSP basada en nonce o hash

Así funcionan los dos tipos de CSP estricta:

CSP basada en nonce

Con una CSP basada en nonce, generas un número al azar en el tiempo de ejecución y lo incluyes en la CSP y asociarla con cada etiqueta de secuencia de comandos de tu página. Un atacante no pueden incluir ni ejecutar una secuencia de comandos maliciosa en tu página, ya que tendrían que adivina el número al azar correcto para esa secuencia de comandos. Esto solo funciona si el número no se puede adivinar y se genera recientemente en el tiempo de ejecución para cada respuesta.

Usa una CSP basada en nonce para las páginas HTML renderizadas en el servidor. En estas páginas, puedes crear un nuevo número al azar para cada respuesta.

CSP basada en hash

Para una CSP basada en hash, el hash de cada etiqueta de secuencia de comandos intercalada se agrega a la CSP. Cada secuencia de comandos tiene un hash diferente. Un atacante no puede incluir ni ejecutar una aplicación secuencia de comandos en tu página, porque el hash de esa secuencia tendría que estar en el CSP para que se ejecute.

Usa una CSP basada en hash para páginas HTML publicadas de forma estática o aquellas que se deben se almacenó en caché. Por ejemplo, puedes usar una CSP basada en hash para sitios web aplicaciones compiladas con frameworks, como Angular, React y otros, que se se entrega estáticamente sin renderización del servidor.

Paso 2: Establece una CSP estricta y prepara tus secuencias de comandos

Cuando configuras una CSP, tienes las siguientes opciones:

  • Modo de solo informes (Content-Security-Policy-Report-Only) o modo de aplicación (Content-Security-Policy). En el modo solo informe, el CSP no bloqueará recursos aún, por lo que no se rompe en tu sitio, pero puedes ver errores y obtener informes para cualquier elemento que se hubiera bloqueado. A nivel local, cuando configurar tu CSP, esto no importa porque ambos modos te muestran errores en la consola del navegador. En todo caso, el modo de aplicación te puede ayudar a encontrar recursos que bloquea tu CSP en borrador, ya que bloquear un recurso puede la página se vea dañada. El modo de solo informes se vuelve más útil más adelante en el proceso. (consulta el Paso 5).
  • Encabezado o etiqueta HTML <meta>. Para el desarrollo local, una etiqueta <meta> puede ser más conveniente para modificar tu CSP y ver rápidamente cómo afecta a tu sitio. Sin embargo, ten en cuenta lo siguiente:
    • Más adelante, cuando implementes tu CSP en producción, te recomendamos configurarlo como un encabezado HTTP.
    • Si quieres configurar tu CSP en modo de solo informes, deberás establecerlo como encabezado, ya que las metaetiquetas de la CSP no admiten el modo de solo informes.

Opción A: CSP basada en nonce

Configurar la siguiente respuesta HTTP Content-Security-Policy encabezado en tu aplicación:

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Genera un nonce para CSP

Un nonce es un número al azar que se usa solo una vez por carga de página. Un nonce basado en La CSP solo puede mitigar XSS si los atacantes no pueden adivinar el valor del nonce. R El nonce de la CSP debe cumplir con lo siguiente:

  • Un valor aleatorio criptográficamente seguro (idealmente, más de 128 bits de longitud)
  • Recientemente generado para cada respuesta
  • Codificación en base64

Estos son algunos ejemplos de cómo agregar un nonce de CSP en frameworks del servidor:

const app = express();

app.get('/', function(request, response) {
  // Generate a new random nonce value for every response.
  const nonce = crypto.randomBytes(16).toString("base64");

  // Set the strict nonce-based CSP response header
  const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`;
  response.set("Content-Security-Policy", csp);

  // Every <script> tag in your application should set the `nonce` attribute to this value.
  response.render(template, { nonce: nonce });
});

Agrega un atributo nonce a los elementos <script>

Con una CSP basada en nonce, cada elemento <script> debe tienen un atributo nonce que coincide con el nonce aleatorio de salida especificado en el encabezado de CSP. Todas las secuencias de comandos pueden tener el mismo nonce. El primer paso es agregar estos atributos a todas las secuencias de comandos CSP lo permite.

Opción B: Encabezado de respuesta de CSP basado en hash

Configurar la siguiente respuesta HTTP Content-Security-Policy encabezado en tu aplicación:

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Para varias secuencias de comandos intercaladas, la sintaxis es la siguiente: 'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'

Cargue secuencias de comandos originadas de forma dinámica

Debido a que los hashes de la CSP son compatibles con los navegadores solo para secuencias de comandos intercaladas, debe cargar todas las secuencias de comandos de terceros de forma dinámica mediante una secuencia de comandos intercalada. Los hashes para secuencias de comandos de origen no son compatibles con todos los navegadores.

Ejemplo de cómo intercalar tus secuencias de comandos.
Permitido por CSP
<script>
  var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];

  scripts.forEach(function(scriptUrl) {
    var s = document.createElement('script');
    s.src = scriptUrl;
    s.async = false; // to preserve execution order
    document.head.appendChild(s);
  });
</script>
Para permitir que se ejecute esta secuencia de comandos, debes calcular el hash de la secuencia de comandos intercalada. y agregarlo al encabezado de respuesta de CSP, reemplazando el {HASHED_INLINE_SCRIPT} marcador de posición. Para reducir la cantidad de hashes, puedes combinarlos todos intercalados secuencias de comandos en una sola. Para ver esto en acción, consulta este ejemplo y su código.
Bloqueada por el CSP
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
La CSP bloquea estas secuencias de comandos porque solo las secuencias de comandos intercaladas pueden generar un hash.

Consideraciones sobre la carga de secuencias de comandos

El ejemplo de secuencia de comandos intercalada agrega s.async = false para garantizar que que foo se ejecuta antes de bar, incluso si bar se carga primero. En este fragmento, s.async = false no bloquea el analizador mientras se cargan las secuencias de comandos, ya que se agregan de forma dinámica. El analizador se detiene solo mientras se ejecutan las secuencias de comandos, ya que que lo haría para async secuencias de comandos. Sin embargo, con este fragmento, ten en cuenta:

  • Una o ambas secuencias de comandos pueden ejecutarse antes de que el documento termine descargas. Si deseas que el documento esté listo para el momento en que se ejecuten, espera el evento DOMContentLoaded antes de anexas las secuencias de comandos. Si esto provoca un problema de rendimiento porque el Las secuencias de comandos no comienzan a descargarse con suficiente anticipación, usa las etiquetas de precarga que aparece más temprano en la página.
  • defer = true no realiza ninguna acción. Si lo necesitas tu rendimiento, ejecuta la secuencia de comandos manualmente cuando sea necesario.

Paso 3: Refactoriza las plantillas HTML y el código del cliente

Controladores de eventos intercalados (como onclick="…" y onerror="…") y URI de JavaScript (<a href="javascript:…">) se puede usar para ejecutar secuencias de comandos. Esto significa que se un atacante que encuentra un error XSS puede inyectar este tipo de HTML y ejecutar JavaScript: Una CSP basada en nonce o hash prohíbe el uso de este tipo de lenguaje de marcado. Si tu sitio usa alguno de estos patrones, deberás refactorizarlos para que sean alternativas.

Si habilitaste la CSP en el paso anterior, podrás ver los incumplimientos de la CSP en la consola cada vez que CSP bloquea un patrón incompatible.

Informes de incumplimiento de la CSP en la consola para desarrolladores de Chrome
Errores de consola para código bloqueado.

En la mayoría de los casos, la solución es sencilla:

Cómo refactorizar controladores de eventos intercalados

Permitido por CSP
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
CSP permite controladores de eventos que se registran con JavaScript.
Bloqueada por el CSP
<span onclick="doThings();">A thing.</span>
La CSP bloquea los controladores de eventos intercalados.

Refactoriza los URIs de javascript:

Permitido por CSP
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
CSP permite controladores de eventos que se registran con JavaScript.
Bloqueada por el CSP
<a href="javascript:linkClicked()">foo</a>
La CSP bloquea JavaScript: los URIs.

Quitar eval() de JavaScript

Si tu aplicación usa eval() para convertir serializaciones de cadenas JSON en JS debes refactorizar esas instancias en JSON.parse(), que también es más rápido.

Si no puedes quitar todos los usos de eval(), aún puedes establecer un modelo estricto basado en nonce pero debes usar la palabra clave de CSP 'unsafe-eval', lo que hace que tu un poco menos segura.

Puedes encontrar estos y más ejemplos de esta refactorización en esta CSP estricta codelab:

Paso 4 (opcional): Agrega resguardos para admitir versiones anteriores del navegador

Navegadores compatibles

  • Chrome: 52.
  • Borde: 79.
  • Firefox: 52.
  • Safari: 15.4.

Origen

Si necesitas ofrecer compatibilidad con versiones anteriores del navegador, haz lo siguiente:

  • Para usar strict-dynamic, debes agregar https: como resguardo para versiones anteriores más recientes de Safari. Cuando lo hagas:
    • Todos los navegadores compatibles con strict-dynamic ignoran el resguardo de https:. por lo que esto no reducirá la solidez de la política.
    • En navegadores antiguos, las secuencias de comandos de origen externo solo pueden cargarse si provienen de un origen HTTPS. Esto es menos seguro que una CSP estricta, pero evita algunas causas comunes de XSS, como inyecciones de URI de javascript:.
  • Para garantizar la compatibilidad con versiones de navegador muy antiguas (más de 4 años), puedes agregar unsafe-inline como resguardo Todos los navegadores recientes ignoran unsafe-inline. si hay un nonce o un hash de la CSP.
Content-Security-Policy:
  script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';

Paso 5: Implementa tu CSP

Luego de confirmar que tu CSP no bloquea ninguna secuencia de comandos legítima en tu desarrollo local, puedes implementar tu CSP en la etapa de pruebas y, luego, en entorno de producción:

  1. (Opcional) Implementa tu CSP en modo de solo informes con el Encabezado Content-Security-Policy-Report-Only. El modo de solo informes es útil para probar un cambio potencialmente rotundo, como un CSP nuevo en producción, antes de comenzar a aplicar restricciones de CSP. En el modo de solo informes, el CSP afectan el comportamiento de tu app, pero el navegador aún genera errores de consola e informes de incumplimiento cuando encuentra patrones incompatibles con tu CSP para que puedas ver qué habría fallado para tus usuarios finales. Para ver más consulta API de Reporting.
  2. Cuando estés seguro de que el CSP no interrumpirá el sitio para los usuarios finales, implementa tu CSP con el encabezado de respuesta Content-Security-Policy. Mié recomienda configurar la CSP con un encabezado HTTP del lado del servidor, ya que es más seguro que una etiqueta <meta>. Después de que completes este paso, tu CSP comenzará proteger tu app de XSS.

Limitaciones

Por lo general, una CSP estricta proporciona una capa adicional de seguridad sólida que ayuda a mitigar el XSS. En la mayoría de los casos, el CSP reduce la superficie de ataque de manera significativa, al Se rechazan los patrones peligrosos, como los URIs de javascript:. Sin embargo, según el tipo de CSP que usas (nonces, hashes, con o sin 'strict-dynamic'), puede hay casos en los que CSP tampoco protege tu app:

  • Si no aprovechas una secuencia de comandos, pero sí hay una inyección directamente en el cuerpo o el El parámetro src de ese elemento <script>.
  • Si hay inyecciones en las ubicaciones de las secuencias de comandos creadas dinámicamente (document.createElement('script')), incluso en cualquier función de biblioteca que crean script nodos del DOM según los valores de sus argumentos. Esta incluye algunas APIs comunes, como .html() de jQuery y .get() y .post() en jQuery < 3.0
  • Si hay inyecciones de plantillas en aplicaciones antiguas de AngularJS. Un atacante que se puede insertar en una plantilla de AngularJS, ejecutar JavaScript arbitrario.
  • Si la política contiene 'unsafe-eval', las inyecciones en eval(), setTimeout() y algunas otras APIs que se usan con poca frecuencia.

Los desarrolladores e ingenieros de seguridad deben prestar especial atención a estos de patrones de seguridad durante las revisiones de código y las auditorías de seguridad. Puedes encontrar más detalles en estos casos en Política de Seguridad del Contenido: Un lío exitoso entre el endurecimiento y la mitigación.

Lecturas adicionales