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 asistencia de 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 presentó recientemente.

El trabajo de implementación se realizó en el transcurso de 2 pasantías: 1. Durante el primero, creamos el marco de trabajo 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 Trusted Types junto con algunas funciones especializadas de Herramientas para desarrolladores para la depuración de Trusted Types.

¿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 de los ataques de secuencia de comandos entre sitios (XSS). Para obtener una introducción detallada a CSP, lee aquí.

Una CSP muy nueva es la política Trusted Types(TT), que permite un análisis dinámico que puede prevenir sistemáticamente una gran clase de ataques de inyección en sitios web. Para lograr esto, TT admite que un sitio web controle su código JavaScript para permitir que solo ciertos tipos de elementos se asignen a receptores de DOM, como internalHTML.

Un sitio web puede activar una política de seguridad del contenido si incluye 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 de una página.

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

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

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

El objetivo de este trabajo fue mejorar la experiencia de depuración de problemas de CSP. Al considerar los problemas nuevos, el equipo de Herramientas para desarrolladores sigue a grandes rasgos este proceso:

  1. Definición de historias de usuario. Identifica un conjunto de historias de usuario en el frontend de Herramientas para desarrolladores que abarque cómo un desarrollador web necesitaría investigar el problema.
  2. Implementación de frontend. En función de las historias de los usuarios, identifique qué datos se necesitan 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étera).
  3. Detección de problemas. Identifica las partes del navegador en las que se puede detectar el problema en Chrome e instrumenta el lugar para informar un problema, incluida la información relevante del paso (2).
  4. Guarda y muestra los problemas. Almacena los problemas en un lugar adecuado y haz que estén disponibles para Herramientas para desarrolladores una vez que se abra
  5. Diseña el texto de los problemas. Piensa en un texto explicativo que ayude al desarrollador web a comprender y, lo que es más importante, a solucionar el problema.

Paso 1: definición de historias de usuario para los problemas de CSP

Antes de empezar nuestro trabajo de implementación, creamos un documento de diseño con historias de usuarios para comprender mejor lo que necesitábamos hacer. Por ejemplo, escribimos la siguiente historia de usuario:


Como desarrollador, que acaba de darse cuenta de que parte de mi sitio web está bloqueada, quiero hacer lo siguiente:- ...averiguar si CSP es el motivo del bloqueo de iframes o imágenes en mi sitio web - ...saber qué directiva de CSP causa el bloqueo de un recurso determinado - ...saber cómo cambiar la CSP de mi sitio web para permitir la visualización de recursos actualmente bloqueados o la ejecución del js bloqueado


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

Con este proceso, aprendimos que la ubicación de la fuente era la información más importante para depurar problemas de la CSP. También consideramos ú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 Elements de Herramientas para desarrolladores también podría ser útil.

Paso 2: Implementación de frontend

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

A continuación, se muestra un 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 básicamente codifica una estructura de datos JSON. Está escrito en un lenguaje sencillo llamado PDL (lenguaje de datos de protocolo). El PDL tiene dos propósitos: Primero, usamos PDL para generar las definiciones de TypeScript en las que se basa el frontend de 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 quizás lo más importante es, generamos una biblioteca de C++ a partir de la definición que maneja la generación y el envío de estas estructuras de datos desde el backend de Chromium para C++ al frontend de Herramientas para desarrolladores. 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 nos habíamos decidido qué información queríamos poner a disposición, necesitábamos analizar dónde obtenerla 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 descrito en la última sección, necesitábamos encontrar el lugar en el que la información estaba realmente disponible en el backend. Afortunadamente, el código de la CSP ya tenía un cuello de botella que se usaba para el modo de solo informes, en el que pudimos conectarse con ContentSecurityPolicy::ReportViolation, que informa los problemas a un extremo de informes (opcional) que se puede configurar en el encabezado HTTP de la CSP. La mayor parte de la información que queríamos informar ya estaba disponible, por lo que no fue necesario realizar grandes cambios en el backend para que la instrumentación funcionara.

Paso 4: Guarda y muestra los problemas

Una pequeña complicación es el hecho de que también queríamos informar problemas que ocurrieron antes de que se abriera 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 Herramientas para desarrolladores está abierto o no. Una vez que se abra Herramientas para desarrolladores (o, en ese caso, se conecte cualquier otro cliente de CDP), todos los problemas registrados previamente se podrán reproducir desde el almacenamiento.

Con esto concluye el trabajo del backend y ahora tuvimos que 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 problemas es un proceso que involucra a varios equipos distintos al nuestro. Por ejemplo, a menudo nos basamos en las estadísticas 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 la manera en que se supone que los desarrolladores web abordarán un determinado tipo de problema. Por lo general, el texto del problema pasa por un proceso de mejora hasta que finaliza.

Por lo general, el equipo de Herramientas para desarrolladores comenzará 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 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.

Cómo depurar problemas de Trusted Types

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

Impresión mejorada de la consola

Cuando trabajamos con objetos de confianza, nos gustaría mostrar al menos la misma cantidad de información que la contraparte no confiable. Lamentablemente, actualmente, 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 del tipo de confianza, el valor que se muestra no es muy útil. En su lugar, nos gustaría tener algo similar a lo que obtienes cuando llamas a .toString(). Para lograr esto, debemos modificar V8 y Blink para introducir un manejo especial para objetos de tipo de confianza.

Si bien, debido a razones históricas, este manejo personalizado se realizó en V8, este enfoque tiene importantes desventajas. Hay muchos objetos que requieren una visualización personalizada, pero cuyo tipo es el mismo en el nivel de JS. Dado que V8 es solo JS, no puede distinguir conceptos que corresponden a una API web, como un tipo de confianza. Por esa razón, V8 debe pedirle ayuda a su integrador (Blink) para distinguirlos.

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

  • Cada incorporaciones puede tener su propia generación de descripciones
  • Es mucho más fácil generar la descripción con 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 pueda redefinir .toString().

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 los incumplimientos de TT forzados activarán una excepción, esta función puede resultar útil. Sin embargo, en situaciones reales, es necesario tener un control más detallado sobre las infracciones de TT. En particular, nos gustaría desglosar únicamente en las infracciones de TT (no en otras excepciones), interrumpir también en el modo de solo informes y distinguir entre los diferentes tipos de infracciones de TT.

Las Herramientas para desarrolladores ya son compatibles con una amplia variedad de puntos de interrupción, por lo que la arquitectura es bastante extensible. Para agregar un nuevo tipo de punto de interrupción, se requieren cambios en el backend (Blink), el CDP y el frontend. Deberíamos ingresar un comando de CDP nuevo, al que llamaremos setBreakOnTTViolation. El frontend utilizará este comando para indicarle al backend qué tipo de incumplimientos de TT debe fallar. El backend, en particular InspectorDOMDebuggerAgent, proporcionará un "sondeo", onTTViolation() que se llamará cada vez que se produzca una infracción de TT. Luego, InspectorDOMDebuggerAgent verificará si ese incumplimiento debe activar un punto de interrupción y, si ese es el caso, enviará un mensaje al frontend para pausar la ejecución.

¿Qué se hizo y cuáles son los próximos pasos?

Desde que se presentaron los problemas que se describen aquí, se realizaron algunos cambios en la pestaña Problemas:

De ahora en adelante, planeamos usar la pestaña Problemas para detectar más problemas, lo que permitirá descargar la consola del flujo de mensajes de error ilegibles a largo plazo.

Descarga los canales de vista previa

Considera usar Canary, Dev o Beta de Chrome como tu navegador de desarrollo predeterminado. Estos canales de vista previa te brindan acceso a las funciones más recientes de Herramientas para desarrolladores, prueba APIs de plataformas web de vanguardia y encuentra problemas en tu sitio antes que tus usuarios.

Cómo comunicarse con el equipo de Herramientas para desarrolladores de Chrome

Usa las siguientes opciones para analizar las nuevas funciones y los cambios en la publicación, o cualquier otra cosa relacionada con Herramientas para desarrolladores.

  • Envíanos tus sugerencias o comentarios a través de crbug.com.
  • Informa un problema en Herramientas para desarrolladores mediante Más opciones   Más   > Ayuda > Informar problemas con Herramientas para desarrolladores en Herramientas para desarrolladores.
  • Envía un tweet a @ChromeDevTools.
  • Deja comentarios en los videos de YouTube de las Novedades de las Herramientas para desarrolladores o en las sugerencias de Herramientas para desarrolladores (videos de YouTube).