Debugging von CSP und vertrauenswürdigen Typen in den Chrome-Entwicklertools implementieren

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

In diesem Blogpost geht es um die Implementierung von DevTools-Unterstützung für die Fehlerbehebung bei Problemen mit der Content Security Policy (CSP) mithilfe des vor Kurzem eingeführten Tabs Probleme.

Die Implementierungsarbeit wurde im Rahmen von zwei Praktika durchgeführt: Während der ersten Phase haben wir das allgemeine Meldesystem entwickelt und die Problemmeldungen für drei CSP-Verstöße entworfen. 2. Im Rahmen der zweiten Version haben wir Probleme mit vertrauenswürdigen Typen sowie einige spezielle DevTools-Funktionen für das Debuggen von vertrauenswürdigen Typen hinzugefügt.

Was ist eine Content Security Policy?

Mit einer Content Security Policy (CSP) können Sie bestimmte Verhaltensweisen auf einer Website einschränken, um die Sicherheit zu erhöhen. Beispielsweise kann die CSP verwendet werden, um Inline-Scripts oder eval zu verbieten. Beides reduziert die Angriffsfläche für Cross-Site Scripting (XSS)-Angriffe. Eine detaillierte Einführung in CSP finden Sie hier.

Eine besonders neue CSP ist die Richtlinie für vertrauenswürdige Typen(Trusted Types, TT). Sie ermöglicht eine dynamische Analyse, mit der eine große Gruppe von Injection-Angriffen auf Websites systematisch verhindert werden kann. Um dies zu erreichen, unterstützt TT eine Website bei der Überwachung ihres JavaScript-Codes, sodass nur bestimmte Arten von Dingen DOM-Senken wie innerHTML zugewiesen werden können.

Eine Website kann eine Content Security Policy aktivieren, indem ein bestimmter HTTP-Header eingefügt wird. Mit dem Header content-security-policy: require-trusted-types-for 'script'; trusted-types default wird beispielsweise die TT-Richtlinie für eine Seite aktiviert.

Jede Richtlinie kann in einem der folgenden Modi ausgeführt werden:

  • enforced mode (erzwungener Modus) – hier ist jeder Richtlinienverstoß ein Fehler.
  • Nur-Meldungsmodus: Die Fehlermeldung wird als Warnung gemeldet, führt aber nicht zu einem Fehler auf der Webseite.

Probleme mit der Content Security Policy auf dem Tab Probleme implementieren

Ziel dieser Arbeit war es, die Fehlerbehebung für CSP-Probleme zu verbessern. Bei der Prüfung neuer Probleme folgt das DevTools-Team ungefähr diesem Prozess:

  1. User Storys definieren Ermitteln Sie im DevTools-Frontend eine Reihe von User Storys, die beschreiben, wie ein Webentwickler das Problem untersuchen müsste.
  2. Frontend-Implementierung Ermitteln Sie anhand der User Storys, welche Informationen für die Untersuchung des Problems im Front-End erforderlich sind (z. B. eine zugehörige Anfrage, der Name eines Cookies, eine Zeile in einem Script oder einer HTML-Datei usw.).
  3. Problemerkennung Ermitteln Sie die Stellen im Browser, an denen das Problem in Chrome erkannt werden kann, und instrumentieren Sie die Stelle, um ein Problem zu melden, einschließlich der relevanten Informationen aus Schritt 2.
  4. Probleme speichern und anzeigen Speichern Sie die Probleme an einem geeigneten Ort und stellen Sie sie den DevTools zur Verfügung, sobald sie geöffnet sind.
  5. Text für Probleme entwerfen Erstellen Sie einen Erläuterungstext, der dem Webentwickler hilft, das Problem zu verstehen und vor allem zu beheben.

Schritt 1: User Storys für CSP-Probleme definieren

Bevor wir mit der Implementierung begannen, haben wir ein Designdokument mit User Storys erstellt, um besser zu verstehen, was wir tun mussten. Beispielsweise haben wir die folgende User Story aufgeschrieben:


Als Entwickler, der gerade festgestellt hat, dass ein Teil meiner Website blockiert ist, möchte ich:- - ...herausfinden, ob die CSP ein Grund für blockierte iFrames / Bilder auf meiner Website ist - ...herausfinden, welche CSP-Anweisung die Blockierung einer bestimmten Ressource verursacht – ...wissen, wie ich die CSP meiner Website so ändern kann, dass sie die Anzeige von derzeit blockierten Ressourcen / die Ausführung von derzeit blockiertem JS ermöglicht.


Um diese User Story zu untersuchen, haben wir einige einfache Beispielwebseiten erstellt, auf denen die für uns interessanten CSP-Verstöße zu sehen sind. Außerdem haben wir uns die Beispielseiten angesehen, um uns mit dem Prozess vertraut zu machen. Hier sind einige Beispiel-Webseiten. Öffnen Sie die Demo mit geöffnetem Tab Probleme:

Dabei haben wir festgestellt, dass der Quellspeicherort die wichtigste Information für die Fehlerbehebung bei CSP-Problemen ist. Wir fanden es auch hilfreich, den zugehörigen iFrame und die Anfrage schnell zu finden, falls eine Ressource blockiert wurde, und dass ein direkter Link zum HTML-Element im Bereich Elemente der Entwicklertools hilfreich sein könnte.

Schritt 2: Frontend-Implementierung

Wir haben diese Informationen in den ersten Entwurf der Informationen umgewandelt, die wir für die Entwicklertools über das Chrome DevTools Protocol (CDP) zur Verfügung stellen möchten:

Unten finden Sie einen Auszug aus 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

Die obige Definition codiert im Wesentlichen eine JSON-Datenstruktur. Sie ist in einer einfachen Sprache namens PDL (Protocol Data Language) geschrieben. PDL wird für zwei Zwecke verwendet. Zuerst generieren wir mit PDL die TypeScript-Definitionen, auf die das DevTools-Frontend angewiesen ist. Aus der obigen PDL-Definition wird beispielsweise die folgende TypeScript-Schnittstelle generiert:

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;
}

Zweitens, und wahrscheinlich wichtiger, generieren wir aus der Definition eine C++-Bibliothek, die das Generieren und Senden dieser Datenstrukturen vom C++-Chromium-Back-End an das DevTools-Front-End übernimmt. Mit dieser Bibliothek kann ein ContentSecurityPolicyIssueDetails-Objekt mit dem folgenden C++-Code erstellt werden:

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

Nachdem wir uns entschieden hatten, welche Informationen wir zur Verfügung stellen wollten, mussten wir herausfinden, wo wir diese Informationen in Chromium finden.

Schritt 3: Problemerkennung

Damit die Informationen dem Chrome DevTools Protocol (CDP) im im letzten Abschnitt beschriebenen Format zur Verfügung gestellt werden konnten, mussten wir den Ort finden, an dem die Informationen im Backend tatsächlich verfügbar waren. Glücklicherweise gab es im CSP-Code bereits ein Nadelöhr, das für den Modus „Nur melden“ verwendet wurde, an dem wir anknüpfen konnten: ContentSecurityPolicy::ReportViolation meldet Probleme an einen (optionalen) Meldeendpunkt, der im CSP-HTTP-Header konfiguriert werden kann. Die meisten Informationen, die wir erfassen wollten, waren bereits verfügbar. Daher waren keine großen Änderungen am Back-End erforderlich, damit unsere Instrumentierung funktionierte.

Schritt 4: Probleme speichern und anzeigen

Eine kleine Komplikation besteht darin, dass wir auch Probleme melden wollten, die vor dem Öffnen der DevTools aufgetreten sind, ähnlich wie bei der Verarbeitung von Konsolennachrichten. Das bedeutet, dass Probleme nicht direkt an das Front-End gemeldet werden, sondern in einem Speicher abgelegt werden, der unabhängig davon, ob DevTools geöffnet ist oder nicht, mit Problemen gefüllt wird. Sobald DevTools geöffnet ist (oder ein anderer CDP-Client verbunden ist), können alle zuvor aufgezeichneten Probleme aus dem Speicher wiedergegeben werden.

Damit ist die Backend-Arbeit abgeschlossen und wir mussten uns darauf konzentrieren, wie das Problem im Frontend aufgedeckt werden kann.

Schritt 5: Problemtext gestalten

Die Formulierung des Problemtexts ist ein Prozess, an dem neben unserem Team noch mehrere andere beteiligt sind. So stützen wir uns beispielsweise oft auf die Erkenntnisse des Teams, das eine Funktion implementiert (in diesem Fall das CSP-Team), und natürlich auf das DevRel-Team, das festlegt, wie Webentwickler mit einer bestimmten Art von Problem umgehen sollen. Der Problemtext wird in der Regel noch etwas überarbeitet, bis er fertig ist.

Normalerweise beginnt das Entwicklertools-Team mit einer groben Vorstellung davon:


## 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/

Nach der Iteration haben wir Folgendes erreicht:

ALT_TEXT_HERE

Wie Sie sehen, wird die Beschreibung durch die Einbeziehung des Feature-Teams und des DevRel-Teams viel klarer und präziser.

CSP-Probleme auf Ihrer Seite können auch auf dem Tab speziell für CSP-Verstöße gefunden werden.

Probleme mit vertrauenswürdigen Typen beheben

Ohne die richtigen Entwicklertools kann die Arbeit mit TT im großen Maßstab eine Herausforderung sein.

Verbessertes Drucken über die Konsole

Wenn wir mit vertrauenswürdigen Objekten arbeiten, möchten wir mindestens die gleiche Menge an Informationen wie für das nicht vertrauenswürdige Gegenstück anzeigen. Leider werden beim Anzeigen eines vertrauenswürdigen Objekts derzeit keine Informationen zum verpackten Objekt angezeigt.

Das liegt daran, dass der in der Konsole angezeigte Wert standardmäßig durch Aufrufen von .valueOf() auf dem Objekt ermittelt wird. Im Fall eines vertrauenswürdigen Typs ist der zurückgegebene Wert jedoch nicht sehr nützlich. Stattdessen möchten wir etwas Ähnliches wie bei einem Anruf bei .toString(). Um dies zu erreichen, müssen wir V8 und Blink ändern, um eine spezielle Behandlung für vertrauenswürdige Objekttypen einzuführen.

Aus historischen Gründen wurde diese benutzerdefinierte Verarbeitung in V8 durchgeführt, ein solcher Ansatz hat jedoch wichtige Nachteile. Es gibt viele Objekte, die eine benutzerdefinierte Anzeige erfordern, deren Typ jedoch auf JS-Ebene identisch ist. Da V8 reines JS ist, kann es keine Konzepte unterscheiden, die einer Web-API entsprechen, z. B. einen vertrauenswürdigen Typ. Aus diesem Grund muss V8 den Einbettungsdienst (Blink) um Hilfe bei der Unterscheidung bitten.

Daher klingt es logisch, diesen Teil des Codes in „Blink“ oder einen Einbettungscode zu verschieben. Neben dem aufgezeigten Problem gibt es viele weitere Vorteile:

  • Für jeden Einbettungspartner kann eine eigene Beschreibung generiert werden.
  • Es ist viel einfacher, die Beschreibung über die Blink API zu generieren.
  • Blink hat Zugriff auf die ursprüngliche Definition des Objekts. Wenn wir also die Beschreibung mit .toString() generieren, besteht kein Risiko, dass .toString() neu definiert wird.

Unterbrechung bei Verstoß (im Modus „Nur Berichterstellung“)

Derzeit ist das Einzige, was Sie tun können, um TT-Verstöße zu beheben, das Setzen von Haltepunkten bei JS-Ausnahmen. Da erzwungene TT-Verstöße eine Ausnahme auslösen, kann diese Funktion nützlich sein. In der Praxis ist jedoch eine detailliertere Steuerung von TT-Verstößen erforderlich. Insbesondere möchten wir nur bei TT-Verstößen (nicht bei anderen Ausnahmen) Unterbrechungen einlegen, auch im reinen Meldemodus, und zwischen den verschiedenen Arten von TT-Verstößen unterscheiden.

DevTools unterstützt bereits eine Vielzahl von Haltepunkten, sodass die Architektur recht erweiterbar ist. Wenn Sie einen neuen Typ von Unterbrechung hinzufügen möchten, sind Änderungen am Backend (Blink), an der CDP und am Frontend erforderlich. Wir sollten einen neuen CDP-Befehl einführen, nennen wir ihn setBreakOnTTViolation. Mit diesem Befehl teilt das Frontend dem Backend mit, welche Art von TT-Verstößen es brechen soll. Das Backend, insbesondere InspectorDOMDebuggerAgent, stellt eine „Probe“ (onTTViolation()) bereit, die jedes Mal aufgerufen wird, wenn ein TT-Verstoß auftritt. Anschließend prüft InspectorDOMDebuggerAgent, ob dieser Verstoß einen Haltepunkt auslösen soll. Ist das der Fall, wird eine Nachricht an das Frontend gesendet, um die Ausführung zu pausieren.

Was wird erledigt und wie geht es weiter?

Seit der Einführung der hier beschriebenen Probleme wurden am Tab Probleme einige Änderungen vorgenommen:

Künftig werden wir auf dem Tab Probleme weitere Probleme anzeigen. So können wir die Konsole langfristig von unleserlichen Fehlermeldungen befreien.

Vorschaukanäle herunterladen

Verwenden Sie als Standard-Entwicklungsbrowser Chrome Canary, Chrome Dev oder Chrome Beta. Diese Vorabversionen bieten Zugriff auf die neuesten DevTools-Funktionen, ermöglichen den Test moderner Webplattform-APIs und helfen Ihnen, Probleme auf Ihrer Website zu finden, bevor Ihre Nutzer sie bemerken.

Chrome-Entwicklertools-Team kontaktieren

Mit den folgenden Optionen können Sie über neue Funktionen, Updates oder andere Themen im Zusammenhang mit den DevTools sprechen.