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

Krzysztof Kotowicz
Krzysztof Kotowicz

Supporto dei browser

  • 83
  • 83
  • x
  • x

Fonte

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

DOM XSS è una delle vulnerabilità di sicurezza web più comuni ed è frequente che i team di sviluppo lo introducano accidentalmente nelle loro app. I TrustedType ti offrono gli strumenti per scrivere, esaminare la sicurezza e mantenere le applicazioni prive di vulnerabilità DOM XSS rendendo sicure funzioni API web pericolose per impostazione predefinita. I tipi attendibili sono disponibili come polyfill per i browser che non li supportano.

Contesto

Per molti anni DOM XSS è stata una delle vulnerabilità per la sicurezza web più diffuse e pericolose.

Esistono due tipi di cross-site scripting (XSS). Alcune vulnerabilità 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 HTML concatenando le stringhe. Utilizza invece librerie di modelli sicure con escape automatico contestuale, insieme a un criterio di sicurezza dei contenuti basato su notazione per ulteriore mitigazione dei bug.

Ora i browser possono anche aiutare a prevenire i messaggi XSS basati su DOM lato client utilizzando TrustedType.

Introduzione all'API

I tipi attendibili funzionano bloccando le seguenti funzioni rischiose del sink. Potresti già riconoscerne alcune, perché i fornitori di browser e i framework web ti stanno già allontanando dall'utilizzo di queste funzionalità per motivi di sicurezza.

I tipi attendibili richiedono di elaborare i dati prima di trasferirli a queste funzioni sink. Utilizzare solo una stringa ha esito negativo, perché il browser non sa se i dati sono affidabili:

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

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

Che cosa fare
anElement.innerHTML = aTrustedHTML;
  
Con i tipi attendibili abilitati, il browser accetta un oggetto TrustedHTML per i sink che prevedono snippet HTML. Sono presenti anche oggetti TrustedScript e TrustedScriptURL per altri sink sensibili.

I tipi attendibili riducono notevolmente la superficie di attacco DOM XSS della tua applicazione. Semplifica le revisioni della sicurezza e 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 attendibili

Prepararsi per i report sulle violazioni dei criteri di sicurezza del contenuto

Puoi eseguire il deployment di un raccoglitore di report, come go-csp-collector open source, oppure utilizzare uno degli equivalenti commerciali. Puoi anche eseguire il debug delle violazioni nel browser:

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

Aggiungi un'intestazione CSP solo per i report

Aggiungi la seguente intestazione di 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 prossima sezione spiega come funziona //my-csp-endpoint.example.

Identifica le violazioni dei tipi attendibili

D'ora in poi, ogni volta che i tipi attendibili rilevano una violazione, il browser invia un report 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 significa che in https://my.url.example/script.js, alla riga 39, è stato chiamato innerHTML con la stringa che inizia con <img src=x. Queste informazioni dovrebbero aiutarti a restringere le parti di codice che potrebbero introdurre DOM XSS e che devono essere modificate.

Correggi le violazioni

Sono disponibili un paio di opzioni per correggere una violazione di tipo attendibile. 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 oppure 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 = '';

Utilizzare una libreria

Alcune librerie generano già tipi attendibili che puoi passare alle funzioni sink. Ad esempio, puoi utilizzare DOMPurify per eliminare 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 il codice HTML convalidato aggregato in un oggetto TrustedHTML in modo che il browser non generi una violazione.

Creare un criterio Trusted Type (Tipo attendibile)

A volte non è possibile rimuovere il codice che causa la violazione e non è disponibile alcuna libreria per cancellare il valore e creare un tipo attendibile per te. In questi casi, puoi creare autonomamente un oggetto Trusted Type.

Per prima cosa, crea una norma. I criteri sono componenti per i tipi attendibili che applicano determinate regole di sicurezza in base all'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 per l'escape dei caratteri HTML-< per impedire la creazione di nuovi elementi HTML.

Utilizza il criterio nel seguente modo:

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

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 rete CDN. In tal 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 il tipo attendibile.

Passa all'applicazione dei criteri di sicurezza del contenuto

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

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

Indipendentemente da quanto sia complessa la tua applicazione web, l'unica cosa che può introdurre una vulnerabilità DOM XSS è il codice in uno dei tuoi criteri, ed è possibile bloccarlo ulteriormente limitando la creazione dei criteri.

Per approfondire