Lo scripting cross-site basato su DOM (DOM XSS) si verifica quando i dati di un
elemento controllato dall'utente (ad esempio un nome utente o un URL di reindirizzamento ricavato dal frammento URL) raggiungono un destinatario, ovvero una funzione come eval()
o un settatore di proprietà come .innerHTML
che può eseguire codice JavaScript arbitrario.
L'XSS DOM è una delle vulnerabilità di sicurezza web più comuni ed è normale che i team di sviluppo lo introducano accidentalmente nelle loro app. Tipi attendibili ti forniscono gli strumenti per scrivere, eseguire la revisione della sicurezza e mantenere le applicazioni prive di vulnerabilità XSS DOM rendendo sicure per impostazione predefinita le funzioni API web pericolose. I tipi attendibili sono disponibili come polyfill per i browser che non li supportano ancora.
Sfondo
Per molti anni, gli XSS DOM sono stati 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 evitare gli attacchi XSS lato server, non generare HTML concatenando stringhe. Utilizza invece librerie di modelli con estrazione automatica contestuale sicura, insieme a un criterio Content Security basato su nonce per una mitigazione aggiuntiva dei bug.
Ora i browser possono anche contribuire a prevenire gli attacchi XSS lato client basati su DOM utilizzando tipi attendibili.
Introduzione all'API
I tipi attendibili funzionano bloccando le seguenti funzioni di destinazione rischiose. Potresti già riconoscerne alcune, perché i fornitori di browser e i framework web ti sconsigliano già di utilizzare queste funzionalità per motivi di sicurezza.
- Manipolazione dello script:
<script src>
e impostazione dei contenuti di testo degli elementi<script>
. - Generare HTML da una stringa:
- Esecuzione dei contenuti del plug-in:
- Compilazione del codice JavaScript in fase di esecuzione:
eval
setTimeout
setInterval
new Function()
I tipi attendibili richiedono di elaborare i dati prima di trasmetterli a queste funzioni di destinazione. 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;
Trusted Types riduce notevolmente la superficie di attacco DOM XSS della tua applicazione. Semplifica le revisioni di sicurezza e ti consente di applicare i controlli di sicurezza basati sui tipi eseguiti durante la compilazione, il linting o il bundling del codice in fase di esecuzione nel browser.
Come utilizzare i tipi attendibili
Prepararsi ai report sulle violazioni dei criteri di sicurezza del contenuto
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 log personalizzati e violazioni di debug 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();
oppure aggiungendo un gestore di eventi:
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 ai 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
.
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 l'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"
}
}
Indica che in https://my.url.example/script.js
, riga 39, innerHTML
è stato chiamato con la stringa che inizia con <img src=x
. Queste informazioni dovrebbero aiutarti a restringere le parti di codice che potrebbero introdurre XSS DOM e che devono essere modificate.
Correggere le violazioni
Esistono un paio di opzioni per correggere una violazione di Trusted Type. Puoi rimuovere il codice in questione, utilizzare una libreria, creare un criterio di tipo attendibile o, come ultima soluzione, creare un criterio predefinito.
Riscrivere il codice in questione
È 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 di destinazione. Ad esempio, puoi utilizzare DOMPurify per eseguire la sanitizzazione di 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 sanificato racchiuso 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 convalidare il valore e creare un tipo attendibile. In questi casi, puoi creare autonomamente un oggetto Trusted Type.
Per prima cosa, crea un criterio. I criteri sono fabbriche per Trusted Types che applicano determinate regole di sicurezza ai 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 relativa funzione createHTML()
. Le regole definite eseguono la codifica HTML dei caratteri <
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; // '<img src=x onerror=alert(1)>'
Utilizzare un criterio predefinito
A volte non puoi modificare il codice in questione, ad esempio se carichi una biblioteca 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 dei criteri di sicurezza del contenuto
Quando l'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à dell'applicazione web, l'unica cosa che può introdurre una vulnerabilità XSS DOM è il codice di uno dei tuoi criteri e puoi bloccare ulteriormente questo problema limitando la creazione dei criteri.