Poslední dobou pracuji hodně na Trusted Types, což je návrh, jak zabránit DOM XSS. Pomocí Trusted Types lze nastavit, že DOM API, která se dají použít pro spuštění kódu (jako např. přiřazení do innerHTML
nebo zavolání document.write
), už nebudou přijímat řetězec (který mohl být podstrčen útočníkem), ale jen jeden z Trusted Types (např. TrustedHTML u API pracujících s HTML), který jiná část aplikace bezpečně vytvořila.
Trusted Types už podporuje prohlížeč Google Chrome, pokud se spustí pomocí google-chrome-unstable --enable-blink-features=TrustedDOMTypes
(přepínače fungují i se stabilní verzí Chrome, jen nejsou opraveny některé chyby). V ostatních prohlížečích lze použít polyfill. Trusted Types mají ambici stát se běžným webovým API dostupným ve všech prohlížečích a použitelným ve všech aplikacích.
Jednotlivé typy v Trusted Types jsou jen jednoduché obálky nad řetězci. Existují čtyři typy:
innerHTML
.script.text
.script.src
.a.href
.Hodnoty těchto typů se dají vytvořit jedině voláním funkcí v TrustedTypesPolicy, která se získá pomocí TrustedTypes.createPolicy(name, policy)
:
var unsafePolicy = TrustedTypes.createPolicy('unsafe', {
// funkce vrací řetězec, ze kterého prohlížeč vytvoří TrustedHTML
createHTML: function (s) {
return s;
}
});
el.innerHTML = unsafePolicy.createHTML('<b>test</b>');
Takto vytvořená policy sama o sobě moc užitečná není, vytvoří TrustedHTML z jakékoliv řetězce. Vtip je ale v tom, že referenci na ni si můžeme držet jen v kódu, o kterém víme, že je bezpečný, např. v goog.html.SafeHtml. Policy daného jména můžeme vytvořit jen jednu. Při zavolání TrustedTypes.createPolicy
se jménem, které už jsme použili, dojde k chybě.
Kromě toho můžeme vytvořit i obecně použitelné policy, které např. sanitizují HTML kód, ověří, jestli spustitelné URL pochází z našeho serveru, nebo zkontrolují, jestli protokol obecného URL je HTTP nebo HTTPS. Takto obecně použitelné policy můžeme zveřejnit pomocí třetího parametru funkce createPolicy
a následně je získat pomocí TrustedTypes.getExposedPolicy(name)
:
TrustedTypes.createPolicy('sanitize', {
createHTML: function (html) {
return sanitizeHTML(html);
},
createScriptURL: function (url) {
const parsed = new URL(url, document.baseURI);
if (parsed.origin == 'https://mycdn.example') {
return URL.toString();
}
throw new TypeError('Invalid URL.');
},
createURL: function (url) {
const parsed = new URL(url, document.baseURI);
if (/^https?:$/.test(parsed.protocol)) {
return URL.toString();
}
throw new TypeError('Invalid URL.');
}
}, /* exposed= */ true);
var sanitizePolicy = TrustedTypes.getExposedPolicy('sanitize');
No a konečně lze definovat tzv. default policy, která se automaticky použije, pokud se DOM API zavolají s řetězcem:
TrustedTypes.createPolicy('default', {
createHTML: function (html) {
return 'default:' + html;
}
}, /* exposed= */ true);
el.innerHTML = 'test'; // přiřadí 'default:test'
Default policy lze použít v přechodném období, kdy aplikace na Trusted Types přechází. Typicky vrátí řetězec nezměněný a případně provede nějaké logování. Po této přechodné fázi bych doporučoval default policy vypnout a všude pracovat s Trusted Types explicitně. Případně lze default policy použít pro knihovny třetích stran, které Trusted Types zatím nepodporují.
Jak byste asi uhodli, policy umožňuje definovat čtyři funkce odpovídající jednotlivým typům: createHTML, createScript, createScriptURL a createURL.
Trusted Types můžeme používat, aniž bychom prohlížeč museli nějak konfigurovat (pokud je podporuje nebo pokud jsme použili polyfill). Pokud ale chceme zvýšit bezpečnost, musíme nastavit, které policy dovolíme ve své aplikaci používat. To se v Chrome dělá pomocí hodnoty trusted-types v hlavičce Content-Security-Policy:
<?php // povolí vytváření jakýchkoliv policies header("Content-Security-Policy: trusted-types *"); // povolí vytvoření policies s názvy 'default' a 'sanitize' header("Content-Security-Policy: trusted-types default sanitize"); ?>
Hodnota *
povolí jakékoliv policies. Při pokusu použít v omezených DOM API řetězec se zkusí zavolat default policy. Pokud neexistuje, dojde k chybě. Kromě *
lze uvést i mezerami oddělené názvy povolených policies. Při pokusu vytvořit jinou policy dojde k chybě.
Do budoucna se pravděpodobna způsob vynucování Trusted Types přesune do samostatné hlavičky Trusted-Types. To konec konců určuje i specifikace.
Trusted Types vypadá jako velmi nadějný způsob, jak vyřešit DOM XSS. Kromě toho máme samozřejmě i server XSS, ke kterému dochází, pokud nedůvěryhodný kód do stránky vypíše přímo server.
Moje práce na Trusted Types je velmi různorodá. Spočívá v úpravách specifikace a polyfillu, opravách kódu Chromium, rozšíření conformance pravidel, úpravách Closure Library, změnách JavaScript externs, změnách TypeScript deklarací a samozřejmě také úpravách aplikací Google, aby Trusted Types začaly používat. Projekt vede koto a kromě mě na něm pracuje ještě několik dalších vývojářů.
Diskuse je zrušena z důvodu spamu.