Il cross-site scripting basato sul DOM (DOM XSS) si verifica quando i dati provenienti da una origine controllata dall'utente (come un nome utente o un URL di reindirizzamento tratto dal frammento di URL) raggiungono un sink, ovvero una funzione come eval()
o un setter di proprietà come .innerHTML
che può eseguire codice JavaScript arbitrario.
Il cross-site scripting (XSS) basato sul DOM è una delle vulnerabilità di sicurezza web più comuni ed è frequente che i team di sviluppo lo introducano accidentalmente nelle loro app. Trusted Types ti offre gli strumenti per scrivere, rivedere la sicurezza e mantenere le applicazioni prive di vulnerabilità XSS basate su DOM rendendo sicure per impostazione predefinita le funzioni API web pericolose. Trusted Types è disponibile come polyfill per i browser che non lo supportano ancora.
Sfondo
Per molti anni, DOM XSS è stata una delle vulnerabilità di sicurezza web più diffuse e pericolose.
Esistono due tipi di cross-site scripting. Alcune vulnerabilità XSS sono causate da codice lato server che crea in modo non sicuro il codice HTML che forma il sito web. Altri hanno una causa principale sul client, dove il codice JavaScript chiama funzioni pericolose con contenuti controllati dall'utente.
Per prevenire XSS lato server, non generare HTML concatenando stringhe. Utilizza invece librerie di modelli con escape automatico contestuale sicuro, insieme a una Content Security Policy basata su nonce per una maggiore mitigazione dei bug.
Ora i browser possono anche contribuire a prevenire gli attacchi XSS (cross-site scripting) basati su DOM lato client utilizzando Trusted Types.
Introduzione alle API
I tipi attendibili funzionano bloccando le seguenti funzioni sink rischiose. Potresti già riconoscerne alcuni, perché i fornitori di browser e framework web ti impediscono già di utilizzare queste funzionalità per motivi di sicurezza.
- Manipolazione degli script:
<script src>
e impostazione del contenuto testuale degli elementi<script>
. - Generazione di HTML da una stringa:
- Esecuzione dei contenuti dei plug-in:
- Compilazione del codice JavaScript in fase di runtime:
eval
setTimeout
setInterval
new Function()
I tipi attendibili richiedono l'elaborazione dei dati prima di passarli a queste funzioni sink. L'utilizzo di una sola stringa non va a buon fine perché il browser non sa se i dati sono attendibili:
anElement.innerHTML = location.href;
Per indicare che i dati sono stati elaborati in modo sicuro, crea un oggetto speciale: un tipo attendibile.
anElement.innerHTML = aTrustedHTML;
TrustedHTML
per i sink che prevedono snippet HTML. Sono disponibili anche
gli oggetti TrustedScript
e TrustedScriptURL
per altri
lavandini sensibili.
Trusted Types riduce notevolmente la superficie di attacco XSS (cross-site scripting) basata su DOM 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 linting o il bundling del codice in fase di runtime, nel browser.
Come utilizzare i tipi attendibili
Prepararsi ai report sulle violazioni delle norme di sicurezza dei contenuti
Puoi implementare un raccoglitore di report, ad esempio reporting-api-processor o go-csp-collector open source, oppure utilizzare uno degli equivalenti commerciali. Puoi anche aggiungere la registrazione personalizzata e il debug delle violazioni nel browser utilizzando un 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();
o aggiungendo un listener di eventi:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Aggiungere un'intestazione CSP solo per i report
Aggiungi la seguente intestazione della risposta HTTP ai documenti di cui vuoi eseguire la migrazione a Trusted Types:
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
.
Identificare le violazioni di Trusted Types
D'ora in poi, ogni volta che Trusted Types rileva una violazione, il browser invia un report a un report-uri
configurato. Ad esempio, quando la tua applicazione
trasmette 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 messaggio indica che in https://my.url.example/script.js
alla riga 39, innerHTML
è stato
chiamato con la stringa che inizia con <img src=x
. Queste informazioni dovrebbero aiutarti
a restringere il campo delle parti di codice che potrebbero introdurre DOM XSS e che devono essere modificate.
Correggere le violazioni
Esistono un paio di opzioni per correggere una violazione di Trusted Types. Puoi rimuovere il codice incriminato, utilizzare una libreria, creare una policy Trusted Types o, come ultima risorsa, creare una policy predefinita.
Riscrivere il codice incriminato
È possibile che il codice non conforme non sia più necessario o possa essere riscritto senza le funzioni che causano le violazioni:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
Utilizzare una libreria
Alcune librerie generano già tipi attendibili che puoi passare alle funzioni sink. Ad esempio, puoi utilizzare DOMPurify per sanificare 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 pulito racchiuso in un oggetto TrustedHTML
in modo che il browser
non generi una violazione.
Creare una policy Trusted Types
A volte non puoi rimuovere il codice che causa la violazione e non esiste una libreria per sanificare il valore e creare un tipo attendibile per te. In questi casi, puoi creare autonomamente un oggetto Trusted Type.
Innanzitutto, crea un criterio. Le policy sono fabbriche di Trusted Types 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, '<')
});
}
Questo codice crea un criterio denominato myEscapePolicy
che può produrre oggetti TrustedHTML
utilizzando la funzione createHTML()
. Le regole definite eseguono l'escape HTML dei caratteri <
per impedire la creazione di nuovi elementi HTML.
Utilizza la norma nel seguente modo:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Utilizzare una norma predefinita
A volte non puoi modificare il codice incriminato, 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 ovunque venga utilizzata una stringa in un sink che accetta solo
il tipo attendibile.
Passare all'applicazione di Content Security Policy
Quando la tua applicazione non genera più violazioni, puoi iniziare a applicare i tipi attendibili:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Ora, indipendentemente dalla complessità della tua applicazione web, l'unica cosa che può introdurre una vulnerabilità DOM XSS è il codice in uno dei tuoi criteri e puoi renderlo ancora più sicuro limitando la creazione di criteri.