Previeni le vulnerabilità cross-site scripting basate su DOM con TrustedType

Krzysztof Kotowicz
Krzysztof Kotowicz

Supporto dei browser

  • 83
  • 83
  • x
  • x

Origine

Il cross-site scripting (DOM XSS) basato su DOM si verifica quando i dati provenienti da una sorgente controllata dall'utente (come un nome utente o un URL di reindirizzamento preso dal frammento URL) raggiungono un sink, ovvero una funzione come eval() o un set di proprietà come .innerHTML in grado di eseguire codice JavaScript arbitrario.

DOM XSS è una delle vulnerabilità di sicurezza web più comuni ed è comune per i team di sviluppo introdurlo accidentalmente nelle proprie app. I tipi attendibili forniscono gli strumenti per scrivere, controllare la sicurezza e mantenere le applicazioni libere dalle vulnerabilità DOM XSS rendendo sicure per impostazione predefinita le funzioni API web pericolose. I tipi di attendibilità sono disponibili come polyfill per i browser che non li supportano ancora.

Contesto

Per molti anni, DOM XSS è stato una delle vulnerabilità di sicurezza web più diffuse e pericolose.

Esistono due tipi di cross-site scripting (XSS). Alcune vulnerabilità di XSS sono causate da codice lato server che crea in modo non sicuro il codice HTML che forma il sito web. Altre hanno una causa principale sul client, dove il codice JavaScript chiama funzioni pericolose con contenuti controllati dall'utente.

Per impedire l'XSS lato server, non generare codice HTML concatenando stringhe. Utilizza invece librerie di modelli sicuri con escape automatico contestuale, insieme a un criterio di sicurezza del contenuto basato su nonce per una ulteriore mitigazione dei bug.

Ora i browser possono anche contribuire a impedire gli attacchi XSS basati su DOM lato client utilizzando Tipi attendibili.

Introduzione all'API

I tipi attendibili bloccano le seguenti funzioni sink rischiose. Probabilmente già ne riconosci alcuni, perché i fornitori di browser e i framework web ti impediscono già di utilizzare queste funzionalità per motivi di sicurezza.

I tipi di attendibilità richiedono di elaborare i dati prima di trasmetterli a queste funzioni sink. L'utilizzo solo di una stringa non riesce perché il browser non sa se i dati sono affidabili:

Cosa non fare
anElement.innerHTML  = location.href;
Se i tipi attendibili sono abilitati, il browser genera un errore TypeError e impedisce l'utilizzo di un sink XSS DOM con una stringa.

Per indicare che i dati sono stati elaborati in modo sicuro, crea un oggetto speciale: Trusted Type.

Che cosa fare
anElement.innerHTML = aTrustedHTML;
  
Quando l'opzione Tipi attendibili è abilitata, il browser accetta un oggetto TrustedHTML per i sink che prevedono snippet HTML. Esistono anche oggetti TrustedScript e TrustedScriptURL per altri sink sensibili.

I tipi attendibili riducono in modo significativo la superficie di attacco DOM XSS della tua applicazione. Semplifica le revisioni della sicurezza e ti consente di applicare i controlli di sicurezza basati sul tipo eseguiti durante la compilazione, il lint o il raggruppamento del codice in fase di runtime nel browser.

Come utilizzare i tipi di attendibilità

Preparati per le segnalazioni di violazione dei criteri di sicurezza del contenuto

Puoi eseguire il deployment di un raccoglitore di report, ad esempio reporting-api-processor open source o go-csp-collector, oppure utilizzare uno degli equivalenti commerciali. Puoi anche aggiungere il logging personalizzato e il debug delle violazioni nel browser utilizzando ReportingObserver:

const observer = new ReportingObserver((reports, observer) => {
    for (const report of reports) {
        if (report.type !== 'csp-violation' ||
            report.body.effectiveDirective !== 'require-trusted-types-for') {
            continue;
        }

        const violation = report.body;
        console.log('Trusted Types Violation:', violation);

        // ... (rest of your logging and reporting logic)
    }
}, { buffered: true });

observer.observe();

oppure aggiungendo un listener di eventi:

document.addEventListener('securitypolicyviolation',
    console.error.bind(console));

Aggiungi un'intestazione CSP solo per i report

Aggiungi la seguente intestazione della risposta HTTP ai documenti di cui vuoi eseguire la migrazione a Tipi attendibili:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Ora tutte le violazioni vengono segnalate a //my-csp-endpoint.example, ma il sito web continua a funzionare. La sezione successiva spiega come funziona //my-csp-endpoint.example.

Identifica le violazioni dei tipi di attendibilità

D'ora in poi, ogni volta che i tipi attendibili rilevano una violazione, il browser invia una segnalazione a un report-uri configurato. Ad esempio, quando l'applicazione passa una stringa a innerHTML, il browser invia il seguente report:

{
"csp-report": {
    "document-uri": "https://my.url.example",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 39,
    "column-number": 12,
    "source-file": "https://my.url.example/script.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
}
}

Questo indica che nella riga 39 di https://my.url.example/script.js, innerHTML è stato chiamato con la stringa che inizia con <img src=x. Queste informazioni dovrebbero aiutarti a restringere le parti del codice che potrebbero introdurre XSS DOM e che devono essere modificate.

Correggi le violazioni

Esistono un paio di opzioni per correggere una violazione relativa al tipo di attendibilità. Puoi rimuovere il codice in questione, utilizzare una libreria, creare un criterio Trusted Type o, come ultima risorsa, creare un criterio predefinito.

Riscrivi il codice in questione

È possibile che il codice non conforme non sia più necessario o che possa essere riscritto senza le funzioni che causano le violazioni:

Che cosa fare
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
Cosa non fare
el.innerHTML = '<img src=xyz.jpg>';

Utilizzare una libreria

Alcune librerie generano già dei tipi di attendibilità che puoi passare alle funzioni sink. Ad esempio, puoi utilizzare DOMPurify per purificare uno snippet HTML, rimuovendo i payload XSS.

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify supporta i tipi attendibili e restituisce HTML convalidato aggregato in un oggetto TrustedHTML in modo che il browser non generi una violazione.

Crea un criterio di tipo Attendibile

A volte non puoi rimuovere il codice che causa la violazione e non esiste una libreria per sanificare il valore e creare un Trusted Type per te. In questi casi, puoi creare autonomamente un oggetto Trusted Type.

Innanzitutto, crea una norma. I criteri sono fabbriche per i tipi di attendibilità che applicano determinate regole di sicurezza al loro input:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

Questo codice crea un criterio chiamato myEscapePolicy in grado di produrre oggetti TrustedHTML utilizzando la sua funzione createHTML(). Le regole definite hanno caratteri di escape HTML < per impedire la creazione di nuovi elementi HTML.

Utilizza questo criterio:

const escaped = escapeHTMLPolicy.createHTML('<img src=x 
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x 

Utilizza un criterio predefinito

A volte non è possibile modificare il codice in questione, ad esempio se stai caricando una libreria di terze parti da una CDN. In questo caso, utilizza un criterio predefinito:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

Il criterio denominato default viene utilizzato ogni volta che viene utilizzata una stringa in un sink che accetta solo l'opzione Trusted Type.

Passa all'applicazione del criterio di sicurezza del contenuto

Quando la tua applicazione non produce più violazioni, puoi iniziare ad applicare i tipi attendibili:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

A prescindere dalla complessità della tua applicazione web, l'unica cosa che può introdurre una vulnerabilità DOM XSS è il codice in uno dei tuoi criteri, che puoi bloccare ulteriormente limitando la creazione dei criteri.

Per approfondire