Implementazione del debug CSP e TrustedType in Chrome DevTools

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

Questo blog post riguarda l'implementazione del supporto di DevTools per il debug dei problemi relativi alla Content Security Policy (CSP) con l'aiuto della scheda Problemi introdotta di recente.

Il lavoro di implementazione è stato svolto nel corso di due stage: 1. Durante la prima, abbiamo creato il framework di reporting generale e progettato i messaggi relativi ai problemi per tre problemi di violazione della CSP. 2. Durante la seconda, abbiamo aggiunto i problemi relativi ai tipi attendibili insieme ad alcune funzionalità di DevTools specializzate per il debug dei tipi attendibili.

Che cos'è una Content Security Policy?

La Content Security Policy (CSP) consente di limitare determinati comportamenti in un sito web per aumentare la sicurezza. Ad esempio, la CSP può essere utilizzata per non consentire gli script incorporati o eval, riducendo così la superficie di attacco per gli attacchi di cross-site scripting (XSS). Per un'introduzione dettagliata alla CSP, leggi qui.

Una CSP particolarmente nuova è la policy Trusted Types(TT), che consente un'analisi dinamica in grado di prevenire sistematicamente un'ampia classe di attacchi di injection sui siti web. Per raggiungere questo obiettivo, TT supporta un sito web nel controllo del suo codice JavaScript per consentire l'assegnazione di solo determinati tipi di elementi ai sink DOM come innerHTML.

Un sito web può attivare una Content Security Policy includendo una particolare intestazione HTTP. Ad esempio, l'intestazione content-security-policy: require-trusted-types-for 'script'; trusted-types default attiva la policy TT per una pagina.

Ogni policy può operare in una di queste modalità:

  • modalità di applicazione forzata: ogni violazione delle policy è un errore;
  • Modalità solo report: segnala il messaggio di errore come avviso, ma non causa un errore nella pagina web.

Implementazione dei problemi relativi alla Content Security Policy nella scheda Problemi

L'obiettivo di questo lavoro era migliorare l'esperienza di debug per i problemi relativi alla CSP. Quando esamina i nuovi problemi, il team di DevTools segue approssimativamente questa procedura:

  1. Definizione delle storie degli utenti. Identifica un insieme di storie dell'utente nel frontend di DevTools che copra il modo in cui uno sviluppatore web dovrebbe esaminare il problema.
  2. Implementazione del frontend. In base alle storie dell'utente, identifica quali informazioni sono necessarie per l'indagine del problema nel front-end (ad es. una richiesta correlata, il nome di un cookie, una riga in uno script o un file HTML e così via).
  3. Rilevamento dei problemi. Identifica i punti del browser in cui è possibile rilevare il problema in Chrome e configura il punto per segnalare un problema, includendo le informazioni pertinenti del passaggio (2).
  4. Salva e visualizza i problemi. Archivia i problemi in un luogo appropriato e rendili disponibili per DevTools una volta aperto
  5. Progettazione del testo dei problemi. Crea un testo esplicativo che aiuti lo sviluppatore web a comprendere e, soprattutto, a risolvere il problema

Passaggio 1: definisci le storie degli utenti per i problemi relativi alla CSP

Prima di iniziare il lavoro di implementazione, abbiamo creato un documento di progettazione con storie degli utenti per comprendere meglio cosa dovevamo fare. Ad esempio, abbiamo scritto la seguente storia dell'utente:


In qualità di sviluppatore che ha appena scoperto che alcune parti del mio sito web sono bloccate, voglio:- …scoprire se CSP è un motivo per cui gli iframe / le immagini sono bloccati sul mio sito web - …scoprire quale direttiva CSP causa il blocco di una determinata risorsa - …sapere come modificare il CSP del mio sito web per consentire la visualizzazione delle risorse attualmente bloccate / l'esecuzione del codice JS attualmente bloccato.


Per esplorare questa storia dell'utente, abbiamo creato alcune semplici pagine web di esempio che mostravano le violazioni della CSP che ci interessavano e le abbiamo esplorate per familiarizzare con la procedura. Ecco alcune pagine web di esempio (apri la demo con la scheda Problemi aperta):

Utilizzando questa procedura, abbiamo appreso che la posizione di origine era l'informazione più importante per il debug dei problemi relativi alla CSP. Abbiamo anche ritenuto utile trovare rapidamente l'iframe e la richiesta associati nel caso in cui una risorsa fosse bloccata e che un link diretto all'elemento HTML nel riquadro Elementi di DevTools potesse essere utile.

Passaggio 2: implementazione del frontend

Abbiamo trasformato queste informazioni nella prima bozza delle informazioni che volevamo rendere disponibili a DevTools tramite il Chrome DevTools Protocol (CDP):

Di seguito è riportato l'estratto di 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 definizione precedente codifica essenzialmente una struttura di dati JSON. È scritto in un linguaggio semplice chiamato PDL (Protocol Data Language). Il PDL viene utilizzato per due scopi. Innanzitutto, utilizziamo PDL per generare le definizioni TypeScript su cui si basa il frontend di DevTools. Ad esempio, la definizione PDL precedente genera la seguente interfaccia 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;
}

In secondo luogo, e probabilmente ancora più importante, generiamo una libreria C++ dalla definizione che gestisce la generazione e l'invio di queste strutture di dati dal backend Chromium C++ al frontend DevTools. Utilizzando questa libreria, è possibile creare un oggetto ContentSecurityPolicyIssueDetails utilizzando il seguente snippet di codice C++:

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

Una volta stabilito quali informazioni volevamo rendere disponibili, abbiamo dovuto capire dove recuperarle da Chromium.

Passaggio 3: rilevamento dei problemi

Per rendere le informazioni disponibili per il Chrome DevTools Protocol (CDP) nel formato descritto nell'ultima sezione, abbiamo dovuto trovare il punto in cui le informazioni erano effettivamente disponibili nel backend. Fortunatamente, il codice CSP aveva già un collo di bottiglia utilizzato per la modalità solo generazione di report, a cui potevamo collegarci: ContentSecurityPolicy::ReportViolation segnala i problemi a un endpoint di generazione di report (facoltativo) che può essere configurato nell'intestazione HTTP CSP. La maggior parte delle informazioni che volevamo segnalare era già disponibile, quindi non sono state necessarie grandi modifiche nel backend per il funzionamento della nostra strumentazione.

Passaggio 4: salva e visualizza i problemi

Una piccola complicazione è il fatto che volevamo anche segnalare i problemi che si sono verificati prima dell'apertura di DevTools, in modo simile a come vengono gestiti i messaggi della console. Ciò significa che non segnaliamo i problemi direttamente al frontend, ma utilizziamo uno spazio di archiviazione che viene compilato con i problemi indipendentemente dal fatto che DevTools sia aperto o meno. Una volta aperto DevTools (o, se è per questo, collegato qualsiasi altro client CDP), tutti i problemi registrati in precedenza possono essere riprodotti dallo spazio di archiviazione.

Questo ha concluso il lavoro di backend e ora dovevamo concentrarci su come mostrare il problema nel frontend.

Passaggio 5: progetta il testo dei problemi

La progettazione del testo dei problemi è un processo che coinvolge diversi team oltre al nostro. Ad esempio, spesso ci affidiamo alle informazioni del team che implementa una funzionalità (in questo caso il team CSP) e, naturalmente, del team DevRel, che progetta il modo in cui gli sviluppatori web dovrebbero affrontare un determinato tipo di problema. Il testo del problema di solito viene perfezionato fino al completamento.

In genere, il team di DevTools inizia con una bozza approssimativa di ciò che immagina:


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

Dopo l'iterazione, siamo arrivati a:

ALT_TEXT_HERE

Come puoi vedere, il coinvolgimento del team della funzionalità e di DevRel rende la descrizione molto più chiara e precisa.

I problemi relativi alla CSP sulla tua pagina possono essere rilevati anche nella scheda dedicata specificamente alle violazioni della CSP.

Debug dei problemi relativi ai tipi attendibili

Lavorare con i TT su larga scala può essere difficile senza gli strumenti per sviluppatori giusti.

Stampa della console migliorata

Quando lavoriamo con oggetti attendibili, vogliamo mostrare almeno la stessa quantità di informazioni della controparte non attendibile. Purtroppo, al momento, quando viene visualizzato un oggetto attendibile, non vengono mostrate informazioni sull'oggetto con wrapping.

Questo perché il valore visualizzato nella console viene preso dalla chiamata di .valueOf() sull'oggetto per impostazione predefinita. Tuttavia, nel caso di Trusted Type, il valore restituito non è molto utile. Invece, vorremmo avere qualcosa di simile a ciò che si ottiene quando si chiama .toString(). Per raggiungere questo obiettivo, dobbiamo modificare V8 e Blink per introdurre una gestione speciale per gli oggetti Trusted Types.

Sebbene per motivi storici questa gestione personalizzata sia stata eseguita in V8, questo approccio presenta svantaggi significativi. Esistono molti oggetti che richiedono una visualizzazione personalizzata, ma il cui tipo è lo stesso a livello di JS. Poiché V8 è puro JS, non è in grado di distinguere concetti che corrispondono a un'API web, come un Trusted Type. Per questo motivo, V8 deve chiedere aiuto al suo incorporatore (Blink) per distinguerli.

Pertanto, spostare quella parte del codice in Blink o in qualsiasi incorporatore sembra una scelta logica. Oltre al problema esposto, ci sono molti altri vantaggi:

  • Ogni embedder può avere la propria generazione di descrizioni
  • È molto più semplice generare la descrizione tramite l'API Blink
  • Blink ha accesso alla definizione originale dell'oggetto. Pertanto, se utilizziamo .toString() per generare la descrizione, non c'è il rischio che .toString() venga ridefinito.

Interruzione in caso di violazione (in modalità Solo report)

Al momento, l'unico modo per eseguire il debug delle violazioni di TT è impostare i punti di interruzione sulle eccezioni JS. Poiché le violazioni dei TT applicati attiveranno un'eccezione, questa funzionalità può essere in qualche modo utile. Tuttavia, negli scenari reali è necessario un controllo più granulare sulle violazioni del TT. In particolare, vorremmo interrompere l'esecuzione solo in caso di violazioni di TT (non di altre eccezioni), anche in modalità solo generazione di report, e distinguere tra i diversi tipi di violazioni di TT.

DevTools supporta già un'ampia gamma di punti di interruzione, quindi l'architettura è piuttosto estensibile. L'aggiunta di un nuovo tipo di punto di interruzione richiede modifiche nel backend (Blink), nel CDP e nel frontend. Dovremmo introdurre un nuovo comando CDP, chiamiamolo setBreakOnTTViolation. Questo comando verrà utilizzato dal frontend per indicare al backend su quali tipi di violazioni del TT deve interrompersi. Il backend, in particolare InspectorDOMDebuggerAgent, fornirà un "probe", onTTViolation(), che verrà chiamato ogni volta che si verifica una violazione del TT. Quindi, InspectorDOMDebuggerAgent verificherà se la violazione deve attivare un punto di interruzione e, in tal caso, invierà un messaggio al frontend per mettere in pausa l'esecuzione.

Cosa è stato fatto e cosa succederà?

Da quando sono stati introdotti i problemi descritti qui, la scheda Problemi ha subito alcune modifiche:

In futuro, prevediamo di utilizzare la scheda Problemi per mostrare più problemi, il che consentirà di scaricare dalla console il flusso di messaggi di errore illeggibili nel lungo periodo.

Scarica i canali in anteprima

Valuta la possibilità di utilizzare Chrome Canary, Dev o Beta come browser di sviluppo predefinito. Questi canali di anteprima ti consentono di accedere alle funzionalità più recenti di DevTools, di testare le API della piattaforma web all'avanguardia e di trovare i problemi sul tuo sito prima che lo facciano gli utenti.

Contatta il team di Chrome DevTools

Utilizza le seguenti opzioni per discutere di nuove funzionalità, aggiornamenti o qualsiasi altro argomento relativo a DevTools.