Deze blogpost gaat over de implementatie van DevTools-ondersteuning voor het oplossen van problemen met het Content Security Policy (CSP) met behulp van het onlangs geïntroduceerde tabblad Problemen.
De implementatie is uitgevoerd tijdens 2 stages: 1. Tijdens de eerste fase hebben we het algemene rapportageframework gebouwd en de probleemmeldingen voor 3 CSP-schendingsproblemen ontworpen. 2. Tijdens de 2e fase hebben we problemen met vertrouwde typen toegevoegd, naast een aantal gespecialiseerde DevTools-functies voor foutopsporing in vertrouwde typen.
Wat is een Content Security Policy?
Met Content Security Policy (CSP) kunt u bepaald gedrag op een website beperken om de beveiliging te verbeteren. Je kunt CSP bijvoorbeeld gebruiken om inline scripts of eval niet toe te staan. Beide verminderen het risico op aanvallen met cross-site scripting (XSS). Lees hier een gedetailleerde inleiding tot CSP.
Een bijzonder nieuw CSP is het beleid Vertrouwde typen(TT), dat een dynamische analyse mogelijk maakt die systematisch een grote klasse van code-injectieaanvallen op websites kan voorkomen. Hiervoor ondersteunt TT een website bij het controleren van de JavaScript-code, zodat alleen bepaalde typen dingen kunnen worden toegewezen aan DOM-sinks zoals innerHTML.
Een website kan een Content Security Policy activeren door een bepaalde HTTP-header op te nemen. De kop
content-security-policy: require-trusted-types-for 'script'; trusted-types default
activeert bijvoorbeeld het TT-beleid voor een pagina.
Elk beleid kan in een van deze modi werken:
- handhavingsmodus: elke beleidsschending is een fout,
- Alleen-rapportagemodus: De foutmelding wordt gerapporteerd als een waarschuwing, maar veroorzaakt geen fout op de webpagina.
Problemen met de implementatie van Content Security Policy op het tabblad Problemen
Het doel van dit werk was om de foutopsporing voor CSP-problemen te verbeteren. Als het DevTools-team nieuwe problemen overweegt, volgt het grofweg dit proces:
- Gebruikersverhalen definiëren. Stel een reeks gebruikersverhalen op in de DevTools-frontend die beschrijven hoe een webontwikkelaar het probleem zou moeten onderzoeken.
- Front-end implementatie. Bepaal op basis van de gebruikersverhalen welke informatie nodig is om het probleem in de frontend te onderzoeken (bijvoorbeeld een gerelateerd verzoek, de naam van een cookie, een regel in een script of html-bestand, enzovoort).
- Probleemdetectie. Identificeer de plaatsen in de browser waar het probleem kan worden gedetecteerd in Chrome en instrumenteer de plaats om een probleem te melden, inclusief de relevante informatie uit stap (2).
- Sla de problemen op en geef ze weer. Sla de problemen op een geschikte plaats op en maak ze beschikbaar voor DevTools zodra deze is geopend
- De tekst van de problemen ontwerpen. Bedenk een verklarende tekst die de webontwikkelaar helpt het probleem te begrijpen en, nog belangrijker, op te lossen
Stap 1: Gebruikersverhalen definiëren voor CSP-problemen
Voordat we met de implementatie begonnen, hebben we een ontwerpdocument met gebruikersverhalen gemaakt om beter te begrijpen wat we moesten doen. We hebben bijvoorbeeld het volgende gebruikersverhaal opgeschreven:
Als ontwikkelaar die net heeft ontdekt dat sommige delen van mijn website zijn geblokkeerd, wil ik:- ...achterhalen of CSP een reden is voor geblokkeerde iframes / afbeeldingen op mijn website - ...te weten komen welke CSP-instructie de blokkering van een bepaalde bron veroorzaakt - ...weten hoe ik het CSP van mijn website kan wijzigen om de weergave van momenteel geblokkeerde bronnen / uitvoering van momenteel geblokkeerde js toe te staan.
Om dit gebruikersverhaal te verkennen, hebben we een aantal eenvoudige voorbeeldwebpagina's gemaakt die de CSP-schendingen vertoonden waarin we geïnteresseerd waren. We hebben de voorbeeldpagina's verkend om zelf vertrouwd te raken met het proces. Hier zijn enkele voorbeelden van webpagina's (open de demo met het tabblad Problemen geopend):
- CSP-problemen
- Schendingen van Trusted Types
- Schendingen van vertrouwde typen: modus Alleen rapportage
Tijdens dit proces hebben we geleerd dat de bronlocatie de belangrijkste informatie is om CSP-problemen op te lossen. We vonden het ook handig om snel het bijbehorende iframe en verzoek te vinden als een bron werd geblokkeerd. Een rechtstreekse link naar het HTML-element in het deelvenster Elementen van DevTools kan ook nuttig zijn.
Stap 2: Front-end implementatie
We hebben dit inzicht omgezet in het eerste concept van de informatie die we beschikbaar wilden maken voor DevTools via het Chrome DevTools-protocol (CDP):
Hieronder staat het fragment uit 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
De bovenstaande definitie codeert in wezen een json-gegevensstructuur. Het is geschreven in een eenvoudige taal, PDL (protocol data language). PDL wordt voor 2 doeleinden gebruikt. Eerst gebruiken we PDL om de TypeScript-definities te genereren waarvan de DevTools-frontend afhankelijk is. De bovenstaande PDL-definitie genereert bijvoorbeeld de volgende TypeScript-interface:
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;
}
Ten tweede, en waarschijnlijk nog belangrijker, genereren we een C++-bibliotheek op basis van de definitie die de generatie en verzending van deze gegevensstructuren van de C++ Chromium-backend naar de DevTools-frontend afhandelt. Met die bibliotheek kan een ContentSecurityPolicyIssueDetails-object worden gemaakt met de volgende C++-code:
protocol::Audits::ContentSecurityPolicyIssueDetails::create()
.setViolatedDirective(d->violated_directive)
.setIsReportOnly(d->is_report_only)
.setContentSecurityPolicyViolationType(BuildViolationType(
d->content_security_policy_violation_type)))
.build();
Nadat we hadden besloten welke informatie we beschikbaar wilden maken, moesten we onderzoeken waar we deze informatie uit Chromium konden halen.
Stap 3: Probleemdetectie
We moesten de plek vinden waar de informatie daadwerkelijk beschikbaar was in de backend om de informatie beschikbaar te maken voor het Chrome DevTools Protocol (CDP) in de indeling die in het laatste gedeelte wordt beschreven. Gelukkig had de CSP-code al een bottleneck die werd gebruikt voor de modus Alleen rapportage, waar we op konden inhaken: ContentSecurityPolicy::ReportViolation meldt problemen aan een (optioneel) rapportage-eindpunt dat kan worden ingesteld in de CSP HTTP-header. De meeste informatie die we wilden rapporteren, was al beschikbaar. Er waren dus geen grote wijzigingen in de backend nodig om onze instrumentatie te laten werken.
Stap 4: Sla de problemen op en geef ze weer
Een kleine complicatie is dat we ook problemen wilden melden die zich voordeden voordat DevTools werd geopend, vergelijkbaar met de manier waarop consoleberichten worden verwerkt. Dit betekent dat we problemen niet meteen aan de frontend melden, maar een opslag gebruiken die onafhankelijk van of DevTools is geopend, wordt gevuld met problemen. Nadat DevTools is geopend (of een andere CDP-client is gekoppeld), kunnen alle eerder geregistreerde problemen opnieuw worden afgespeeld vanuit de opslag.
Hiermee was het werk aan de backend afgerond. Nu moesten we ons richten op hoe we het probleem in de frontend konden tonen.
Stap 5: De tekst voor de problemen ontwerpen
Het ontwerpen van de tekst voor problemen is een proces waarbij naast ons eigen team verschillende teams betrokken zijn. We vertrouwen bijvoorbeeld vaak op inzichten van het team dat een functie implementeert (in dit geval het CSP-team) en natuurlijk het DevRel-team, dat ontwerpt hoe webontwikkelaars met een bepaald type probleem moeten omgaan. De tekst van het probleem wordt meestal verfijnd totdat deze klaar is.
Meestal begint het DevTools-team met een ruwe schets van wat ze zich voorstellen:
## 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/
Na herhaling kwamen we uit op:

Zoals je ziet, wordt de beschrijving veel duidelijker en nauwkeuriger als je het functieteam en DevRel erbij betrekt.
CSP-problemen op je pagina kunnen ook worden gevonden op het tabblad dat speciaal is bedoeld voor CSP-schendingen.
Problemen met Vertrouwde typen opsporen
Werken met TT op grote schaal kan een uitdaging zijn zonder de juiste tools voor ontwikkelaars.
Verbeterde consoleafdrukken
Als we met vertrouwde objecten werken, willen we minstens dezelfde hoeveelheid informatie tonen als voor de niet-vertrouwde tegenhanger. Als er een vertrouwd object wordt getoond, wordt er op dit moment geen informatie over het verpakte object getoond.
Dit komt omdat de waarde die in de console wordt getoond, standaard wordt opgehaald door .valueOf() aan te roepen voor het object. In het geval van Trusted Type is de geretourneerde waarde niet erg nuttig. In plaats daarvan willen we iets vergelijkbaars met wat je krijgt als je .toString() belt. Hiervoor moeten we V8 en Blink aanpassen om een speciale verwerking voor vertrouwde typeobjecten te introduceren.
Hoewel deze aangepaste verwerking om historische redenen in V8 werd uitgevoerd, heeft deze aanpak belangrijke nadelen. Er zijn veel objecten die een aangepaste weergave vereisen, maar waarvan het type hetzelfde is op JS-niveau. Omdat V8 pure JS is, kan het geen concepten onderscheiden die overeenkomen met een web-API, zoals een Trusted Type. Daarom moet V8 de embedder (Blink) om hulp vragen om ze te onderscheiden.
Het is daarom een logische keuze om dat deel van de code te verplaatsen naar Blink of een andere insluiter. Naast het blootgelegde probleem zijn er nog veel andere voordelen:
- Elke insluiter kan een eigen beschrijving genereren
- Het is veel makkelijker om de beschrijving te genereren via de Blink API
- Blink heeft toegang tot de oorspronkelijke definitie van het object. Als we
.toString()gebruiken om de beschrijving te genereren, is er dus geen risico dat.toString()opnieuw wordt gedefinieerd.
Onderbreken bij schending (in de modus Alleen rapportage)
Op dit moment is de enige manier om TT-schendingen op te sporen door breekpunten in te stellen voor JS-uitzonderingen. Omdat afgedwongen TT-schendingen een uitzondering activeren, kan deze functie nuttig zijn. In de praktijk heb je echter meer gedetailleerde controle nodig over TT-schendingen. We willen met name alleen onderbreken bij TT-schendingen (niet bij andere uitzonderingen), ook onderbreken in de modus Alleen rapportage en onderscheid maken tussen de verschillende typen TT-schendingen.
DevTools ondersteunt al een breed scala aan breekpunten, dus de architectuur is behoorlijk uitbreidbaar. Als je een nieuw type breekpunt wilt toevoegen, moet je wijzigingen aanbrengen in de backend (Blink), CDP en de frontend.
We moeten een nieuwe CDP-opdracht introduceren, laten we die setBreakOnTTViolation noemen. Deze opdracht wordt door de frontend gebruikt om de backend te vertellen bij welke soort TT-schendingen deze moet stoppen. De backend, met name InspectorDOMDebuggerAgent, biedt een 'probe', onTTViolation() die wordt aangeroepen elke keer dat er een TT-schending plaatsvindt. Daarna controleert InspectorDOMDebuggerAgent of die schending een breekpunt moet activeren. Als dat het geval is, wordt er een bericht naar de frontend gestuurd om de uitvoering te onderbreken.
Wat is er gedaan en wat is de volgende stap?
Sinds de hier beschreven problemen zijn geïntroduceerd, is het tabblad Problemen flink veranderd:
- De onderlinge verbondenheid met andere deelvensters in DevTools is verbeterd.
- De rapportage van een aantal andere problemen is verplaatst naar het tabblad Problemen: onder andere laag contrast, vertrouwde webactiviteit, quirks-modus, API voor attributierapportage en CORS-gerelateerde problemen.
- Er is een mogelijkheid geïntroduceerd om problemen te verbergen
In de toekomst willen we het tabblad Problemen gebruiken om meer problemen te tonen. Zo kunnen we op de lange termijn de onleesbare foutmeldingen uit de Console verwijderen.
De voorbeeldkanalen downloaden
Overweeg Chrome Canary, Dev of Bèta te gebruiken als je standaardbrowser voor ontwikkeling. Deze previewkanalen geven je toegang tot de nieuwste DevTools-functies, laten je geavanceerde webplatform-API's testen en helpen je problemen op je site te vinden voordat je gebruikers dat doen.
Contact opnemen met het Chrome DevTools-team
Gebruik de volgende opties om de nieuwe functies, updates of iets anders over DevTools te bespreken.
- Stuur feedback en functieverzoeken naar ons via crbug.com.
- Meld een DevTools-probleem via Meer opties > Hulp > Een DevTools-probleem melden in DevTools.
- Tweet naar @ChromeDevTools.
- Laat reacties achter bij YouTube-video's over Wat is er nieuw in DevTools of YouTube-video's met DevTools-tips.