Cómo implementar la CSP y la depuración de Trusted Types en Herramientas para desarrolladores de Chrome

Kateryna Prokopenko
Kateryna Prokopenko
Alfonso Castaño
Alfonso Castaño

Esta entrada de blog trata sobre la implementación de la compatibilidad de las Herramientas para desarrolladores para depurar problemas de la Política de Seguridad del Contenido (CSP) con la ayuda de la pestaña Problemas que se introdujo recientemente.

El trabajo de implementación se realizó en el transcurso de 2 pasantías: 1. Durante la primera, creamos el framework general de informes y diseñamos los mensajes de problemas para 3 problemas de incumplimiento de la CSP. 2. Durante la segunda, agregamos problemas de tipos de confianza junto con algunas funciones especializadas de Herramientas para desarrolladores para la depuración de tipos de confianza.

¿Qué es una política de seguridad del contenido?

La Política de Seguridad del Contenido (CSP) permite restringir ciertos comportamientos en un sitio web para aumentar la seguridad. Por ejemplo, la CSP se puede usar para inhabilitar las secuencias de comandos intercaladas o eval, lo que reduce la superficie de ataque para los ataques de secuencia de comandos entre sitios (XSS). Para obtener una introducción detallada a la CSP, consulta este artículo.

Una CSP particularmente nueva es la política de Tipos de confianza(TT), que habilita un análisis dinámico que puede evitar de forma sistemática una gran clase de ataques de inyección en sitios web. Para lograrlo, los TT admiten un sitio web que controle su código JavaScript para permitir que solo se asignen ciertos tipos de elementos a los receptores del DOM, como innerHTML.

Un sitio web puede activar una política de seguridad del contenido incluyendo un encabezado HTTP específico. Por ejemplo, el encabezado content-security-policy: require-trusted-types-for 'script'; trusted-types default activa la política de TT para una página.

Cada política puede operar en uno de estos modos:

  • modo de aplicación forzosa: en el que cada incumplimiento de política es un error
  • Modo de solo informes: Informa el mensaje de error como una advertencia, pero no provoca un error en la página web.

Problemas de implementación de la política de seguridad del contenido en la pestaña Problemas

El objetivo de este trabajo era mejorar la experiencia de depuración de los problemas de CSP. Cuando se consideran problemas nuevos, el equipo de Herramientas para desarrolladores sigue aproximadamente este proceso:

  1. Definición de historias de usuario. Identifica un conjunto de historias de usuario en el frontend de las Herramientas para desarrolladores que abarque cómo un desarrollador web necesitaría investigar el problema.
  2. Implementación de frontend. Según las historias de usuario, identifica qué información se requiere para investigar el problema en el frontend (p. ej., una solicitud relacionada, el nombre de una cookie, una línea en una secuencia de comandos o un archivo HTML, etc.).
  3. Detección de problemas. Identifica los lugares del navegador en los que se puede detectar el problema en Chrome y prepara el lugar para informar un problema, incluida la información pertinente del paso (2).
  4. Guarda y muestra los problemas. Almacenar los problemas en un lugar adecuado y ponerlos a disposición de las Herramientas para desarrolladores una vez que se abran
  5. Diseña el texto de los problemas. Crea un texto explicativo que ayude al desarrollador web a comprender el problema y, lo que es más importante, a solucionarlo.

Paso 1: Define las historias de usuario para los problemas de CSP

Antes de comenzar nuestro trabajo de implementación, creamos un documento de diseño con historias de usuario para comprender mejor lo que debíamos hacer. Por ejemplo, escribimos la siguiente historia de usuario:


Como desarrollador que acaba de darse cuenta de que algunas partes de mi sitio web están bloqueadas, quiero:- ...averiguar si la CSP es el motivo por el que se bloquean los iframes o las imágenes de mi sitio web - ...saber qué directiva de la CSP provoca el bloqueo de un determinado recurso - ...saber cómo cambiar la CSP de mi sitio web para permitir que se muestren los recursos que están bloqueados actualmente o que se ejecute el código JS que está bloqueado actualmente.


Para explorar esta historia de usuario, creamos algunas páginas web de ejemplo simples que mostraban los incumplimientos de la CSP que nos interesaban y las exploramos para familiarizarnos con el proceso. Estos son algunos ejemplos de páginas web (abre la demostración con la pestaña Problemas abierta):

Con este proceso, aprendimos que la ubicación de origen era la información más importante para depurar los problemas de CSP. También nos pareció útil encontrar rápidamente el iframe y la solicitud asociados en caso de que se bloqueara un recurso, y que un vínculo directo al elemento HTML en el panel Elementos de las Herramientas para desarrolladores también podría ser útil.

Paso 2: Implementación de frontend

Convertimos esta idea en el primer borrador de la información que queríamos poner a disposición de las Herramientas para desarrolladores a través del Protocolo de Herramientas para desarrolladores de Chrome (CDP):

A continuación, se muestra el extracto de third_party/blink/public/devtools_protocol/browser_protocol.pdl

 type ContentSecurityPolicyIssueDetails extends object
   properties
     # The url not included in allowed sources.
     optional string blockedURL
     # Specific directive that is violated, causing the CSP issue.
     string violatedDirective
     boolean isReportOnly
     ContentSecurityPolicyViolationType contentSecurityPolicyViolationType
     optional AffectedFrame frameAncestor
     optional SourceCodeLocation sourceCodeLocation
     optional DOM.BackendNodeId violatingNodeId

La definición anterior codifica, en esencia, una estructura de datos JSON. Está escrito en un lenguaje simple llamado PDL (lenguaje de datos de protocolo). La PDL se usa para dos propósitos. Primero, usamos PDL para generar las definiciones de TypeScript en las que se basa el frontend de las Herramientas para desarrolladores. Por ejemplo, la definición de PDL anterior genera la siguiente interfaz de TypeScript:

export interface ContentSecurityPolicyIssueDetails {
  /**
  * The url not included in allowed sources.
  */
  blockedURL?: string;
  /**
  * Specific directive that is violated, causing the CSP issue.
  */
  violatedDirective: string;
  isReportOnly: boolean;
  contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType;
  frameAncestor?: AffectedFrame;
  sourceCodeLocation?: SourceCodeLocation;
  violatingNodeId?: DOM.BackendNodeId;
}

En segundo lugar, y probablemente lo más importante, generamos una biblioteca de C++ a partir de la definición que se encarga de generar y enviar estas estructuras de datos desde el backend de Chromium de C++ al frontend de DevTools. Con esa biblioteca, se puede crear un objeto ContentSecurityPolicyIssueDetails con el siguiente fragmento de código de C++:

protocol::Audits::ContentSecurityPolicyIssueDetails::create()
  .setViolatedDirective(d->violated_directive)
  .setIsReportOnly(d->is_report_only)
  .setContentSecurityPolicyViolationType(BuildViolationType(
      d->content_security_policy_violation_type)))
  .build();

Una vez que decidimos qué información queríamos que estuviera disponible, tuvimos que explorar de dónde obtener esta información de Chromium.

Paso 3: Detección de problemas

Para que la información esté disponible para el protocolo de Herramientas para desarrolladores de Chrome (CDP) en el formato que se describe en la última sección, tuvimos que encontrar el lugar en el que la información estaba disponible en el backend. Afortunadamente, el código de CSP ya tenía un cuello de botella que se usaba para el modo de solo informes, en el que podíamos conectarnos: ContentSecurityPolicy::ReportViolation informa problemas a un extremo de informes (opcional) que se puede configurar en el encabezado HTTP de CSP. La mayor parte de la información que queríamos informar ya estaba disponible, por lo que no se necesitaron grandes cambios en el backend para que funcionara nuestra instrumentación.

Paso 4: Guarda y muestra los problemas

Una pequeña complicación es el hecho de que también queríamos informar los problemas que ocurrieron antes de que se abrieran las Herramientas para desarrolladores, de manera similar a cómo se manejan los mensajes de la consola. Esto significa que no informamos los problemas de inmediato al frontend, sino que usamos un almacenamiento que se completa con problemas, independientemente de si las Herramientas para desarrolladores están abiertas o no. Una vez que se abren las Herramientas para desarrolladores (o, para el caso, se adjunta cualquier otro cliente de CDP), todos los problemas registrados anteriormente se pueden volver a reproducir desde el almacenamiento.

Con esto, se concluyó el trabajo de backend, y ahora debíamos enfocarnos en cómo mostrar el problema en el frontend.

Paso 5: Diseña el texto de los problemas

El diseño del texto de los problemas es un proceso que involucra a varios equipos además del nuestro. Por ejemplo, a menudo confiamos en la información del equipo que implementa una función (en este caso, sería el equipo de CSP) y, por supuesto, el equipo de DevRel, que diseña cómo se supone que los desarrolladores web deben abordar un determinado tipo de problema. El texto del problema suele pasar por un proceso de perfeccionamiento hasta que se termina.

Por lo general, el equipo de DevTools comienza con un borrador de lo que imagina:


## Header
Content Security Policy: include all sources of your resources in content security policy header to improve the functioning of your site

## General information
Even though some sources are included in the content security policy header, some resources accessed by your site like images, stylesheets or scripts originate from sources not included in content security policy directives.

Usage of content from not included sources is restricted to strengthen the security of your entire site.

## Specific information

### VIOLATED DIRECTIVES
`img-src 'self'`

### BLOCKED URLs
https://imgur.com/JuXCo1p.jpg

## Specific information
https://web.dev/strict-csp/

Después de la iteración, llegamos a lo siguiente:

ALT_TEXT_HERE

Como puedes ver, involucrar al equipo de funciones y a DevRel hace que la descripción sea mucho más clara y precisa.

Los problemas de CSP en tu página también se pueden descubrir en la pestaña dedicada específicamente a los incumplimientos de CSP.

Depura problemas de Trusted Types

Trabajar con TT a gran escala puede ser un desafío sin las herramientas para desarrolladores adecuadas.

Mejoras en la impresión de la consola

Cuando trabajamos con objetos de confianza, queremos mostrar, al menos, la misma cantidad de información que para la contraparte que no es de confianza. Lamentablemente, cuando se muestra un objeto de confianza, no se muestra información sobre el objeto unido.

Esto se debe a que el valor que se muestra en la consola se toma de la llamada a .valueOf() en el objeto de forma predeterminada. Sin embargo, en el caso de Trusted Type, el valor que se devuelve no es muy útil. En cambio, nos gustaría tener algo similar a lo que obtienes cuando llamas a .toString(). Para lograrlo, debemos modificar V8 y Blink para introducir un manejo especial de los objetos de tipo de confianza.

Sin embargo, debido a razones históricas, este manejo personalizado se realizaba en V8, un enfoque que tiene desventajas importantes. Hay muchos objetos que requieren una visualización personalizada, pero cuyo tipo es el mismo a nivel de JS. Como V8 es JS puro, no puede distinguir conceptos que corresponden a una API web, como un tipo de confianza. Por ese motivo, V8 debe pedirle ayuda a su incorporador (Blink) para distinguirlos.

Por lo tanto, mover esa parte del código a Blink o a cualquier incorporador parece una opción lógica. Además del problema expuesto, existen muchos otros beneficios:

  • Cada incorporador puede tener su propia generación de descripciones
  • Es mucho más fácil generar la descripción a través de la API de Blink
  • Blink tiene acceso a la definición original del objeto. Por lo tanto, si usamos .toString() para generar la descripción, no hay riesgo de que se vuelva a definir .toString().

Interrupción por incumplimiento (en el modo de solo informes)

Actualmente, la única forma de depurar los incumplimientos de TT es establecer puntos de interrupción en las excepciones de JS. Dado que las infracciones de TT aplicadas activarán una excepción, esta función puede ser útil de alguna manera. Sin embargo, en situaciones reales, necesitas un control más detallado de los incumplimientos de TT. En particular, nos gustaría detenernos solo en los incumplimientos de TT (no en otras excepciones), detenernos también en el modo de solo informes y distinguir entre los diferentes tipos de incumplimientos de TT.

Las Herramientas para desarrolladores ya admiten una amplia variedad de puntos de interrupción, por lo que la arquitectura es bastante extensible. Agregar un nuevo tipo de punto de interrupción requiere cambios en el backend (Blink), el CDP y el frontend. Deberíamos introducir un nuevo comando de CDP, llamémoslo setBreakOnTTViolation. El frontend usará este comando para indicarle al backend en qué tipo de incumplimientos de TT debe detenerse. El backend, en particular InspectorDOMDebuggerAgent, proporcionará un "sondeo", onTTViolation(), que se llamará cada vez que se produzca un incumplimiento de TT. Luego, InspectorDOMDebuggerAgent verificará si ese incumplimiento debería activar un punto de interrupción y, si es así, enviará un mensaje al frontend para pausar la ejecución.

¿Qué se hizo y qué sigue?

Desde que se introdujeron los problemas que se describen aquí, la pestaña Issues experimentó algunos cambios:

En el futuro, planeamos usar la pestaña Issues para mostrar más problemas, lo que permitirá descargar la consola del flujo de mensajes de error ilegibles a largo plazo.

Descarga los canales de versión preliminar

Considera usar Chrome Canary, Dev o Beta como tu navegador de desarrollo predeterminado. Estos canales de vista previa te brindan acceso a las funciones más recientes de las Herramientas para desarrolladores, te permiten probar las APIs de vanguardia de la plataforma web y te ayudan a encontrar problemas en tu sitio antes que los usuarios.

Comunícate con el equipo de Herramientas para desarrolladores de Chrome

Usa las siguientes opciones para analizar las funciones nuevas, las actualizaciones o cualquier otro tema relacionado con las Herramientas para desarrolladores.