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 der DevTools-Unterstützung für die Fehlerbehebung bei Problemen mit der Content Security Policy (CSP) mithilfe des vor Kurzem eingeführten Tabs Probleme.

Die Implementierung 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. Mit CSP können Sie beispielsweise Inline-Scripts oder eval deaktivieren, wodurch die Angriffsfläche für Cross-Site-Scripting-Angriffe (XSS) verringert wird. Eine ausführliche 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. Dazu unterstützt TT eine Website dabei, ihren JavaScript-Code so zu überwachen, dass DOM-Sinks wie innerHTML nur bestimmte Arten von Elementen zugewiesen werden dürfen.

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:

  • Erzwingungsmodus: Jeder Richtlinienverstoß ist ein Fehler.
  • Nur-Meldemodus: 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 bei CSP-Problemen 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 Folgendes herausfinden: ...ob CSP ein Grund für blockierte Iframes / Bilder auf meiner Website ist ...welche CSP-Richtlinie die Blockierung einer bestimmten Ressource verursacht ...wie ich die CSP meiner Website ändern kann, um die Anzeige derzeit blockierter Ressourcen / die Ausführung derzeit blockierter JS-Scripts zuzulassen.


Um diese User Story zu untersuchen, haben wir einige einfache Beispiel-Webseiten mit den CSP-Verstößen erstellt, die uns interessierten, und uns mit den Beispielseiten vertraut gemacht, 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 Speicherort der Quelle die wichtigste Information für die Fehlerbehebung bei CSP-Problemen ist. Außerdem fanden wir es nützlich, den zugehörigen Iframe und die Anfrage schnell zu finden, falls eine Ressource blockiert wurde. Ein direkter Link zum HTML-Element im Bereich Elemente in den DevTools könnte ebenfalls nützlich sein.

Schritt 2: Front-End-Implementierung

Auf dieser Grundlage haben wir den ersten Entwurf der Informationen erstellt, die wir über das Chrome DevTools Protocol (CDP) in den DevTools verfügbar machen wollten:

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 waren die Back-End-Arbeiten abgeschlossen und wir mussten uns nun darauf konzentrieren, wie wir das Problem im Front-End sichtbar machen.

Schritt 5: Text für Probleme entwerfen

Bei der Formulierung des Problemtexts sind neben unserem Team noch mehrere andere beteiligt. 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 DevTools-Team mit einem groben Entwurf seiner Vorstellung:


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

Bei der Arbeit mit vertrauenswürdigen Objekten möchten wir mindestens die gleiche Menge an Informationen anzeigen wie für das nicht vertrauenswürdige Gegenstück. 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 von „Trusted Type“ ist der zurückgegebene Wert jedoch nicht sehr nützlich. Stattdessen möchten wir etwas Ähnliches wie bei einem Anruf bei .toString(). Dazu müssen wir V8 und Blink ändern, um eine spezielle Verarbeitung für Objekte des vertrauenswürdigen Typs 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 Darstellung erfordern, deren Typ aber 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 seinen Embedder (Blink) um Hilfe bitten, um sie zu unterscheiden.

Daher ist es sinnvoll, diesen Teil des Codes zu Blink oder einem anderen Embedder zu verschieben. Neben dem beschriebenen Problem gibt es noch viele weitere Vorteile:

  • Für jeden Embedder 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 .toString() zum Generieren der Beschreibung verwenden, 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 ist bereits erledigt und was kommt als Nächstes?

Seit der Einführung der hier beschriebenen Probleme hat sich der Tab Probleme erheblich verändert:

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 Ihnen Zugriff auf die neuesten DevTools-Funktionen, ermöglichen es Ihnen, innovative Webplattform-APIs zu testen, 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.