v této části popíšeme některé obecné zásady pro prevenci zranitelností skriptování mezi weby a způsoby použití různých běžných technologií pro ochranu před útoky XSS.
prevence skriptování mezi weby lze obecně dosáhnout pomocí dvou vrstev obrany:
- kódování dat na výstupu
- ověření vstupu při příjezdu
můžete použít Burp Scanner skenovat vaše webové stránky pro četné bezpečnostní chyby, včetně XSS. Burpova špičková logika skenování replikuje akce zkušeného útočníka a je schopna dosáhnout odpovídajícím způsobem vysokého pokrytí zranitelností XSS. Můžete použít Burp Scanner, abyste získali jistotu, že vaše obrana proti útokům XSS funguje efektivně.
kódování dat na výstupu
kódování by mělo být použito přímo před zápisem uživatelem kontrolovatelných dat na stránku, protože kontext, do kterého píšete, určuje, jaký druh kódování musíte použít. Například hodnoty uvnitř řetězce JavaScript vyžadují jiný typ úniku než hodnoty v kontextu HTML.
v kontextu HTML byste měli převést hodnoty bez seznamu povolených na entity HTML:
-
<
převádí na:<
-
>
převádí na:>
v kontextu řetězce JavaScript by nealfanumerické hodnoty měly unikat Unicode:
-
<
převádí na:\u003c
-
>
převádí na:\u003e
někdy budete muset použít více vrstev kódování ve správném pořadí. Chcete-li například bezpečně vložit Uživatelský vstup do obslužného programu událostí, musíte se vypořádat jak s kontextem JavaScriptu, tak s kontextem HTML. Takže musíte nejprve Unicode-uniknout vstupu a pak HTML-kódovat:
<a href="#" onclick="x='This string needs two layers of escaping'">test</a>
ověření vstupu při příjezdu
kódování je pravděpodobně nejdůležitější linií obrany XSS, ale nestačí zabránit zranitelnostem XSS v každém kontextu. Měli byste také ověřit vstup co nejpřísněji v okamžiku, kdy je poprvé přijat od uživatele.
příklady ověření vstupu zahrnují:
- pokud uživatel odešle adresu URL, která bude vrácena v odpovědích, potvrdí, že začíná bezpečným protokolem, jako jsou HTTP a HTTPS. V opačném případě může někdo zneužít váš web pomocí škodlivého protokolu, jako je
javascript
nebodata
. - pokud uživatel zadá hodnotu, která by měla být číselná, potvrdí, že hodnota skutečně obsahuje celé číslo.
- ověření, že vstup obsahuje pouze očekávanou sadu znaků.
ověření vstupu by mělo ideálně fungovat blokováním neplatného vstupu. Alternativní přístup, pokoušet se vyčistit neplatný vstup, aby byl platný, je náchylnější k chybám a je třeba se mu vyhnout, kdykoli je to možné.
Whitelisting vs blacklisting
ověření vstupu by mělo obecně používat Whitelisty spíše než blacklisty. Například místo pokusu o vytvoření seznamu všech škodlivých protokolů (javascript
, data
atd.), jednoduše vytvořte seznam bezpečných protokolů (HTTP, HTTPS) a zakažte vše, co není na seznamu. Tím zajistíte, že se vaše obrana nerozbije, když se objeví nové škodlivé protokoly, a bude méně náchylná k útokům, které se snaží zamlžit neplatné hodnoty, aby se vyhnuly černé listině.
umožnění „bezpečného“ HTML
umožnění uživatelům zveřejňovat značky HTML by se mělo vyhnout, kdykoli je to možné, ale někdy je to obchodní požadavek. Například web blogu může povolit zveřejňování komentářů obsahujících některé omezené značky HTML.
klasickým přístupem je pokusit se odfiltrovat potenciálně škodlivé značky a JavaScript. Můžete to zkusit implementovat pomocí whitelistu bezpečných značek a atributů, ale díky nesrovnalostem v motorech pro analýzu prohlížeče a vtípkům, jako je mutace XSS, je tento přístup velmi obtížné bezpečně implementovat.
nejméně špatnou možností je použít knihovnu JavaScript, která provádí filtrování a kódování v prohlížeči uživatele, například DOMPurify. Jiné knihovny umožňují uživatelům poskytovat obsah ve formátu markdown a převést markdown do HTML. Bohužel všechny tyto knihovny mají čas od času zranitelnosti XSS, takže to není dokonalé řešení. Pokud používáte jeden, měli byste pečlivě sledovat aktualizace zabezpečení.
Poznámka
kromě JavaScriptu může být v některých situacích škodlivý i jiný obsah, jako je CSS a dokonce i běžný HTML.
útoky pomocí škodlivého CSS
jak zabránit XSS pomocí šablonového motoru
mnoho moderních webových stránek používá serverové šablony, jako jsou Twig a Freemarker, k vložení dynamického obsahu do HTML. Ty obvykle definují svůj vlastní únikový systém. Například ve větvičce můžete použít filtr e()
s argumentem definujícím kontext:
{{ user.firstname | e('html') }}
některé další šablony, jako je Jinja a React, ve výchozím nastavení unikají dynamickému obsahu, který účinně zabraňuje většině výskytů XSS.
při hodnocení, zda použít daný modul šablon nebo rámec, doporučujeme pečlivě zkontrolovat funkce escapování.
Poznámka
pokud přímo spojíte vstup uživatele do řetězců šablon, budete zranitelní injekcí šablon na straně serveru, která je často závažnější než XSS.
jak zabránit XSS v PHP
v PHP existuje vestavěná funkce pro kódování entit nazývaných htmlentities
. Tuto funkci byste měli zavolat, abyste unikli vašemu vstupu v kontextu HTML. Funkce by měla být volána se třemi argumenty:
- váš vstupní řetězec.
-
ENT_QUOTES
, což je příznak, který určuje, že všechny uvozovky by měly být kódovány. - znaková sada, která by ve většině případů měla být UTF-8.
například:
<?php echo htmlentities($input, ENT_QUOTES, 'UTF-8');?>
když v kontextu řetězce JavaScript, je třeba Unicode-escape vstup, jak již bylo popsáno. Bohužel, PHP neposkytuje API pro Unicode-uniknout řetězec. Zde je nějaký kód k tomu, že v PHP:
<?php
function jsEscape($str) {
$output = '';
$str = str_split($str);
for($i=0;$i<count($str);$i++) {
$chrNum = ord($str);
$chr = $str;
if($chrNum === 226) {
if(isset($str) && ord($str) === 128) {
if(isset($str) && ord($str) === 168) {
$output .= '\u2028';
$i += 2;
continue;
}
if(isset($str) && ord($str) === 169) {
$output .= '\u2029';
$i += 2;
continue;
}
}
}
switch($chr) {
case "'":
case '"':
case "\n";
case "\r";
case "&";
case "\";
case "<":
case ">":
$output .= sprintf("\u%04x", $chrNum);
break;
default:
$output .= $str;
break;
}
}
return $output;
}
?>
zde je návod, jak používat funkci jsEscape
v PHP:
<script>x = '<?php echo jsEscape($_GET)?>';</script>
Případně můžete použít šablonu motoru.
jak zabránit XSS straně klienta v JavaScriptu
Chcete-li uniknout vstupu uživatele v kontextu HTML v JavaScriptu, potřebujete svůj vlastní kodér HTML, protože JavaScript neposkytuje API pro kódování HTML. Zde je několik příkladů kódu JavaScript, který převádí řetězec na entity HTML:
function htmlEncode(str){
return String(str).replace(//gi, function(c){
return '&#'+c.charCodeAt(0)+';';
});
}
tuto funkci byste pak použili následovně:
<script>document.body.innerHTML = htmlEncode(untrustedValue)</script>
pokud je váš vstup uvnitř řetězce JavaScript, potřebujete kodér, který provádí únik Unicode. Zde je ukázkový kód Unicode:
function jsEscape(str){
return String(str).replace(//gi, function(c){
return '\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
});
}
tuto funkci byste pak použili následovně:
<script>document.write('<script>x="'+jsEscape(untrustedValue)+'";<\/script>')</script>
jak zabránit XSS v jQuery
nejběžnější forma XSS v jQuery je, když předáte Uživatelský vstup voliči jQuery. Weboví vývojáři často používají location.hash
a předávají je selektoru, který by způsobil XSS, protože jQuery by vykreslil HTML. jQuery rozpoznal tento problém a opravil svou voličskou logiku, aby zkontroloval, zda vstup začíná hash. Nyní jQuery vykreslí HTML pouze v případě, že první znak je <
. Pokud předáte nedůvěryhodná data voliči jQuery, ujistěte se, že správně uniknete hodnotě pomocí funkce jsEscape
výše.
zmírnění XSS pomocí content security policy (CSP)
Content security policy (CSP) je poslední obrannou linií proti skriptování mezi weby. Pokud vaše prevence XSS selže, můžete použít CSP ke zmírnění XSS omezením toho, co může útočník udělat.
CSP umožňuje ovládat různé věci, například zda lze načíst externí skripty a zda budou spuštěny inline skripty. Chcete-li nasadit CSP, musíte zahrnout hlavičku odpovědi HTTP s názvem Content-Security-Policy
s hodnotou obsahující vaše zásady.
příklad CSP je následující:
default-src 'self'; script-src 'self'; object-src 'none'; frame-src 'none'; base-uri 'none';
tato zásada určuje, že zdroje, jako jsou obrázky a skripty, lze načíst pouze ze stejného původu jako hlavní stránka. Takže i když útočník může úspěšně vložit užitečné zatížení XSS, může načíst pouze zdroje z aktuálního původu. To výrazně snižuje šanci, že útočník může zneužít zranitelnost XSS.
pokud potřebujete načíst externí zdroje, ujistěte se, že povolujete pouze skripty, které útočníkovi nepomáhají zneužít váš web. Například, pokud jste whitelist určité domény pak útočník může načíst libovolný skript z těchto domén. Pokud je to možné, zkuste hostovat zdroje ve své vlastní doméně.
pokud to není možné, můžete použít zásady založené na hash nebo nonce k povolení skriptů v různých doménách. Nonce je náhodný řetězec, který je přidán jako atribut skriptu nebo prostředku, který bude proveden pouze v případě, že náhodný řetězec odpovídá serveru generovanému. Útočník není schopen odhadnout randomizovaný řetězec, a proto nemůže vyvolat skript nebo prostředek s platným nonce, takže prostředek nebude spuštěn.