Implementazione del debug CSP e TrustedType in Chrome DevTools

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

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

I lavori di implementazione sono stati eseguiti nel corso di due stage: 1. Durante il primo, abbiamo creato il framework generale per i report e progettato i messaggi relativi a tre problemi di violazione dei 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?

I Criteri di sicurezza del contenuto (CSP) consentono di limitare determinati comportamenti in un sito web per aumentare la sicurezza. Ad esempio, CSP può essere utilizzato per non consentire gli script in linea o per non consentire eval, entrambi i quali riducono la superficie di attacco per gli attacchi di cross-site scripting (XSS). Per un'introduzione dettagliata ai CSP, leggi qui.

Un CSP particolarmente nuovo è il criterio Trusted Types(TT), che consente un'analisi dinamica che può impedire sistematicamente una vasta classe di attacchi di inserimento sui siti web. Per raggiungere questo obiettivo, TT aiuta un sito web a controllare il codice JavaScript in modo da consentire l'assegnazione di determinati tipi di elementi solo ai canali DOM come innerHTML.

Un sito web può attivare un criterio di sicurezza dei contenuti includendo una determinata intestazione HTTP. Ad esempio, l'intestazione content-security-policy: require-trusted-types-for 'script'; trusted-types default attiva il criterio TT per una pagina.

Ogni criterio può funzionare in una di queste modalità:

  • modalità applicata: in cui ogni violazione delle norme è un errore,
  • Modalità solo report: segnala il messaggio di errore come avviso, ma non causa un errore nella pagina web.

Implementazione dei problemi relativi ai criteri di sicurezza dei contenuti nella scheda Problemi

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

  1. Definizione delle storie utente. Identifica un insieme di storie utente nel front-end di DevTools che descriva in che modo uno sviluppatore web dovrebbe esaminare il problema.
  2. Implementazione del frontend. In base alle storie utente, identifica quali informazioni sono necessarie per esaminare il 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 il problema può essere rilevato in Chrome e strumentalizza il punto per segnalare un problema, incluse le informazioni pertinenti del passaggio 2.
  4. Salva e visualizza i problemi. Memorizza i problemi in un luogo appropriato e rendili disponibili a 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: definizione delle user story per i problemi relativi ai fornitori di servizi cloud

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


In qualità di sviluppatore, ho appena scoperto che alcune parti del mio sito web sono bloccate e voglio:- - ...scoprire se il 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 JavaScript attualmente bloccato.


Per esplorare questa user story, abbiamo creato alcune semplici pagine web di esempio che presentavano le violazioni del CSP che ci interessavano ed esplorato le pagine di esempio 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 della sorgente era la più importante per il debug dei problemi relativi ai CSP. Abbiamo anche trovato 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 front-end

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

Di seguito è riportato l'estratto da 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 riportata sopra 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 di TypeScript su cui si basa il front-end di DevTools. Ad esempio, la definizione PDL riportata sopra 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 più importante, generiamo una libreria C++ dalla definizione che gestisce la generazione e l'invio di queste strutture di dati dal backend di Chromium C++ al frontend di DevTools. Con questa libreria, è possibile creare un oggetto ContentSecurityPolicyIssueDetails utilizzando il seguente 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 stabilite le informazioni che volevamo rendere disponibili, dovevamo capire dove trovarle in Chromium.

Passaggio 3: rilevamento dei problemi

Per rendere le informazioni disponibili per il protocollo Chrome DevTools (CDP) nel formato descritto nell'ultima sezione, dovevamo trovare il punto in cui le informazioni erano effettivamente disponibili nel back-end. Fortunatamente, il codice CSP aveva già un collo di bottiglia utilizzato per la modalità solo 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 al back-end per il funzionamento della nostra strumentazione.

Passaggio 4: salva e visualizza i problemi

Una piccola complicazione è che volevamo anche segnalare i problemi che si sono verificati prima dell'apertura di DevTools, in modo simile alla gestione dei messaggi della console. Ciò significa che non segnaliamo immediatamente i problemi al front-end, ma utilizziamo uno spazio di archiviazione in cui vengono inseriti i problemi indipendentemente dal fatto che DevTools sia aperto o meno. Una volta aperto DevTools (o collegato qualsiasi altro client CDP), tutti i problemi registrati in precedenza possono essere riprodotti dallo spazio di archiviazione.

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

Passaggio 5: progettazione del testo dei problemi

La progettazione del testo dei problemi è un processo che coinvolge diversi team oltre al nostro. Ad esempio, spesso ci basiamo sulle 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 devono gestire un determinato tipo di problema. Il testo del problema viene generalmente perfezionato fino al completamento.

Di solito il team di DevTools inizia con una bozza 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, abbiamo ottenuto:

ALT_TEXT_HERE

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

I problemi relativi ai CSP nella tua pagina possono essere rilevati anche nella scheda dedicata alle violazioni dei CSP.

Eseguire il debug dei problemi di Trusted Types

Lavorare con i TT su larga scala può essere complicato 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 visualizzate informazioni sull'oggetto con wrapping.

Questo perché il valore visualizzato nella console viene preso dalla chiamata a .valueOf() sull'oggetto per impostazione predefinita. Tuttavia, nel caso di Tipo attendibile, il valore restituito non è molto utile. Vorremmo invece avere qualcosa di simile a quello che ottieni quando chiami .toString(). Per farlo, dobbiamo modificare V8 e Blink per introdurre un'elaborazione speciale per gli oggetti di tipo attendibile.

Anche se per motivi storici questa gestione personalizzata è stata eseguita in V8, questo approccio presenta svantaggi importanti. 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 i concetti che corrispondono a un'API web, come un tipo attendibile. Per questo motivo, V8 deve chiedere aiuto al suo embedder (Blink) per distinguerli.

Pertanto, spostare questa parte di codice in Blink o in qualsiasi altro embedder sembra una scelta logica. A parte il problema esposto, ci sono molti altri vantaggi:

  • Ogni inserzionista può avere la propria generazione di descrizioni
  • È molto più facile 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'è rischio che .toString() venga ridefinito.

Interrompi in caso di violazione (in modalità solo report)

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

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

Cosa è stato fatto e cosa succederà dopo?

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

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

Scaricare i canali di 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 API di piattaforme web all'avanguardia e di trovare i problemi sul tuo sito prima che lo facciano i tuoi utenti.

Contatta il team di Chrome DevTools

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