Přejít k navigační liště

Zdroják » Různé » Jak princezna finálně zatočila s (DOM) XSS

Jak princezna finálně zatočila s (DOM) XSS

Články Různé

Převyprávění přednášky z JSDays 2022. Detailně a se slidy.

Nálepky:

Text vyšel původně na autorově webu.

Detail přednášky

Ta princezna se jmenovala Mozilla a její princ byl Chrome(j). A tihle dva se rozhodli, že už bylo dost všech těch bugů a vyplacených odměn za nahlášení Cross-Site Scriptingu a tak upekli DOMa-based ochranu přímo ve svých výtvorech.

Princezna s princem vám ústy svého tiskového mluvčího a dvorního šaška v jedné osobě poví, co je to ten XSS, že klientskou variantu nezastaví escapování na serveru, a že sanitizér není to mejdlo na ruce. A taky o tom, jak pomohou Trusted Types, a že to všechno není jen pohádka, ale už (skoro) realita.

Přednáška a slajdy jsou včetně ukázek, které si můžete sami vyzkoušet.

Datum a akce

17. února 2022, JSDays 2022 (délka přednášky 60 minut, 17 slajdů)

Slajdy

Jak princezna zatočila s (DOM) XSS

#1 DOM (Document Object Model) XSS (Cross-Site Scripting) je typ XSS útoků, ke kterým dochází v JavaScriptových aplikacích v browserech bez nutnosti doručení zákeřného JavaScriptu v odpovědi ze serveru. Princezna Mozilla a hlavně princ Chrome(j) se rozhodli s tím něco udělat. Tohle je přednáška o blízké budoucnosti.Opáčkross-site scripting

#2 Tohle jsou tři typy XSS:

  1. Stored XSS (někdy též „permanent XSS“)
  2. Reflected (též „temporary“)
  3. DOM XSS (někdy „DOM-based XSS“).

Následuje malé opakování těchto variant, ale nepůjdeme moc do hloubky.Stored XSS: 🐱‍👤 ➡1️⃣➡ 🖥 ➡2️⃣➡ 👩‍💻

#3 Při Stored variantě útočník (ninja kočka vlevo) uloží (krok 1) nebo zastoruje zákeřný JavaScript do nějakého úložiště, typicky databáze, úpravou např. poznámky při objednávce, doručovací adresy apod. a pak aplikace doručí tento JavaScript do browseru (krok 2) každému uživateli, který si prohlédne stránku, která tento zákeřný JavaScript vypíše do browseru bez správného ošetření. JavaScript pak v browseru může ukrást cookie, zobrazit falešný login formulář, posílat HTTP požadavky a skenovat vnitřní síť z browseru běžícího uvnitř této sítě. Co všechno takový JavaScript umí, se dozvíte třeba v mé přednášce o XSS, ukázku pak najdete ve videu od 14. minuty.Edit SSH key, label: <script/>alert("XSS');

#4 Když mám za úkol něco pojmenovat a já nevím, tak píšu alerty. A já nevím dost často, teď třeba zrovna nevím, jak pojmenovat budoucího potomka (dík za optání a jo, „Alert Špaček“ jsem už navrhnout zkusil). Píšu to skoro všude, většinou jen tak, z takové velmi specifické legrace.

The page at https://bitbucket.org says: XSS

#5 Někdy se opravdu zasměju, jindy jen pousměju. Třeba když Bitbucket tenhle můj název SSH klíče pro použití s Gitem vypsal do stránky přesně tak, jak jsem ho nazval. Můj browser poté ten JavaScript spustil a zobrazil se alert. Přišel jsem i na to, jak by se tohle dalo zneužít i proti někomu jinému než proti mně samotnému (takovým „proti mně samotnému“ útokům se říká Self-XSS, velmi často probíhají stylem „otevřete developer tools a do konzole vložte tento kód a budete mít starý Facebook (a já budu mít třeba vaše cookies)“) a výrobce Bitbucketu mě za to zařadil na svou zeď slávy za 2014. Na mém webu je JavaScript třeba i v HTTP hlavičkách i DNS záznamech, protože proč ne, všechno je uživatelský vstup – v některých případech i to může způsobit problém, ale nebojte, můj JS jen zobrazí nějaké ty gify a videa.htmlspecialchars() & Content Security Policy

#6 Obrana proti Stored XSS spočívá ve správném ošetření nebezpečných znaků na serveru pomocí funkcí jako je htmlspecialchars() např. (převádí znaky < > " ' na entity, čímž zruší jejich speciální význam pro HTML) nebo ještě lépe pomocí nějakých šablonovacích systémů, které to escapování (převod na entity) dělají automaticky a vždy – takže na to nemůžete zapomenout, když máte deadline nebo hangover. Escapování se dá považovat za primární úroveň zabezpečení proti XSS, sekundární pak může být třeba Content Security Policy (CSP). Pomocí CSP můžete browseru říci, odkud může stahovat obrázky a fonty, které skripty může spouštět a kam může posílat formulářová data, takže pokud by útočník do stránky nějaký zákeřný JavaScript přece jen dostal, tak ho browser nejspíš nespustí. CSP ale není primární úroveň, jen doplňková. CSP dnes nebudeme detailně probírat, jen se o to trochu, spíš tak nějak mimochodem, otřeme později. Pro další info koukněte na tuhle přednášku o CSP a tu o CSP Level 3.Reflected XSS: 🐱‍👤 ➡1️⃣➡ 👩‍💻 ➡2️⃣➡ 🖥 ➡3️⃣➡ 👩‍💻

#7 Při Reflected XSS variantě útočník nejdříve přiměje uživatele klinout na odkaz, ve kterém je obsažen nějaký zákeřný JavaScript (krok 1). Kliknutím se ten zákeřný JS přenese až do aplikace (krok 2), která ho vypíše zpět do prohlížeče uživatele, který na odkaz kliknul (krok 3). Dochází k jakémusi odrazu zákeřného kód od aplikace zpět do prohlížeče, proto název „reflected“. Představte si to třeba na stránce s výsledky vyhledávání na adrese /search?query=<script>…</script>, která vypíše např. „Výsledky vyhledávání $query“.Stored XSS: htmlspecialchars() & CSP (& XSS Auditor)

#8 Ochrana proti Reflected XSS je v podstatě stejná jako u Stored XSS: převod nebezpečných znaků na entity na serveru pomocí funkcí jako htmlspecialchars() nebo lépe pomocí šablonovacích systémů a Content Security Policy jako další úroveň zabezpečení, kdyby ta primární selhala, viz dříve. V prohlížečích existovala (a v Safari ještě stále existuje od Safari 15.4 už ani tam a naopak ve Firefoxu neexistovala nikdy) ochrana pomocí XSS Auditoru (někdy „XSS Filter“). Ten sledoval, jestli v odchozím požadavku (krok 2 na předchozím slajdu) není něco co vypadá jako JavaScript, a pokud se to vrátilo i zpět ze serveru (v kroku 3), tak to XSS Auditor prohlásil za Reflected XSS a dle nastavení nebo verze prohlížeče stránku buď „opravil“ a zobrazil ji bez toho zákeřného JS, nebo načítání úplně zablokoval a zobrazil celostránkové varování. To zní jako Dobrej Nápad™, akorát že vůbec. Navíc XSS Auditor někteří vývojáři brali jako obecnou ochranu proti XSS, protože „jaká security chyba, hele, mně ten alert browser nezobrazí, žádná chyba tu není“. Navíc XSS Auditor nedokázal vždy poznat, že v požadavku je JS a bylo běžné jeho obcházení. CSP je lepší nástroj, a tak to Auditor měl spočítáno. Nejdříve ho odstranil původní Edge a časem i Chrome a prohlížeče postavené na Chromium (tedy i Chromedge).XS-Leaks: 🐱‍👤 ➡1️⃣➡ 👩‍💻 ➡2️⃣➡ 🖥 ➡3️⃣➡ 👩‍💻 ➡4️⃣➡ 🐱‍👤

#9 XSS Auditor umožňoval získávat nebo prohledávat stránky, které uživateli taková zranitelná aplikace posílala. Proto název XS-Leaks nebo XS-Search, kde XS znamená „Cross-Site“. Představte si, že aplikace uživateli pošle nějaký JS kód (např. var signedin = true) a útočník chce zjistit, jestli je uživatel do takové zranitelné aplikace přihlášen. Pošle uživateli odkaz (1) např. https://example.com/neco?<script>var signedin = true</script>, XSS Auditor detekuje Reflected XSS (2, 3), načtení stránky zablokuje a browser tak nemůže načíst např. obrázek, který tam útočník také vložil. Útočník také může pomocí vkládaných framů zjistit, jak dlouho se stránka načítala, a z toho vyvodit přítomnost zablokovaného JS apod. (4).Google Bug Bounty XSS: $1.2M in 2015-2016

#10 Jenom za hlášení XSS chyb Google za roky 2015–2016 vyplatil 1,2 milionu USD. Mimo jiné to signalizuje, že XSS útoky jsou celkem schopná věc a mohou být celkem zdrcující.Million Dollar Cube v muzeu v Chicagu

#11 Jen pro představu, tohle je prý milion dolarů v jednodolarových bankovkách v muzeu v Chicagu (zdroj).DOM-based XSS bounty

#12 Google neuvádí přesně za co ten milion vyplatil, ačkoliv by se to možná dalo někde postupně dohledat. Nemálo peněz vyplácí i za tu třetí variantu: DOM-based XSS. Tady za tuhle chybu vyplatil hezky kulatou sumu 3133,7 USD. Pro Stored a Reflected XSS už navrhli Content Security Policy jako další úroveň zabezpečení, takže už bylo na čase něco udělat i s DOM XSS.Sanitizer API

#13 Na Sanitizer API spolupracuje Google, Mozilla a Cure53. V současnou chvíli (únor 2022) je v browserech zatím defaultně vypnutý (v Chrome je část API dostupná veřejně od verze 105), protože není ještě zcela dokončena implementace, a podporu je potřeba zapnout v chrome://flags nebo about://config. Sanitizer API je také dostupný pouze pro stránky na HTTPS, přesněji jen pro „secure contexty“. Sanitizer API slouží pro „vyčištění“ HTML, pokud ho chcete v JavaScriptu někam přiřadit nebo zobrazit v nějakém elementu na stránce, aby tam právě někdo nepropašoval nějaký zákeřný JS. Na takové čištění se často používá třeba knihovna DOMPurify shodou okolností (nebo spíš ne) také od Cure53.const s = new Sanitizer(); el.setHTML(text, s); el.innerHTML = s.sanitizeFor('h1', text).innerHTML

#14 Sanitizer nabízí metody pro čištění HTML a prvkům přidává metodu setHTML(). Vyzkoušet si to můžete v podstatě na jakékoliv doméně, ale pojďme na https://example.com/?foo=FOO%3Cimg%20src=x%20onerror=alert(1)%3EBAR.

Parametr foo není vypisován do stránky, ale použijeme ho pro simulaci DOM XSS. Načtěte ten link a do developer tools konzole napište

const el = document.getElementsByTagName('h1')[0]
const param = new URLSearchParams(location.search).get('foo')
el.innerHTML = param

Tím obsah značky H1 nahradíme za obsah parametru foo a neoštřeným zápisem do innerHTML způsobíme DOM-based XSS. Poté to budeme chtít sanitizérovat:

const s = new Sanitizer()
el.setHTML(param, s)

Sanitizer odstranil nebezpečný tag onerror, ale značku IMG ponechal. Stejného výsledku bychom dosáhli zápisem očištěného řetězce do innerHTML takto:

el.innerHTML = s.sanitizeFor('h1', param).innerHTML

Pro odstranění všech HTML značek použijte jinak nakonfigurovaný Sanitizer (nefunguje se setHTML(…, s), jen s sanitizeFor(), prozatím?):

const s = new Sanitizer({allowElements:[]})

A pak už stačí jen projít kód a všechny nebezpečné věci nahradit Sanitizerem 😅DOM-based XSS sink: el.innerHTML

#15 DOM-based XSS vzniká, když se do tzv. „sinků““ zapíše neošetřený vstup. Jedním z takových sinků je vlastnost innerHTML, dalším je např. funkce eval(), obě dokáží spustit libovolný JavaScript (mezi značkami SCRIPT a v onerror atributech apod.) Trusted Types mohou zajistit, aby se takový libovolný JS nespustil.🙅‍♂️ el.innerHTML = '<img decoding=‚ 👉 el.innerHTML = “ width=“800″ height=“450″>

#16 Trusted Types (podporuje zatím jen Chrome, podporu není třeba nijak zapínat, pro jiné browsery existuje tzv. polyfill) umí zakázat zápis libovolných řetězců do sinků a nahradit ho zápisem objektů třídy TrustedHTML. Trusted Types nám pomohou dokázat, že používáme bezpečný zápis, protože ten nebezpečný ani nepůjde použít. Objekty TrustedHTML můžeme vytvářet buď ručně, nebo to celé můžeme nechat na automagice. Trusted Types se dají použít i jen k nalezení sinků, zkuste si to na mém demo webu, všimněte si CSP hlavičky, která vyžadování Trusted Types zapíná (a její „report-only“ varianty, která zajistí, že se stránka bude normálně načítat, ale budou se jen posílat reporty) a direktivy require-trusted-types-for 'script'. Rozklikněte si kód a uvidíte i zápis do sinku innerHTML. Pokud tam tlačítkem Enter any HTML něco zapíšete, browser to sice udělá, ale postěžuje si do konzole a pošle report, který uvidíte pod odkazem Reports.🙅🏽‍♂️ el.innerHTML = '<img decoding=‚ 👉🏽 el.innerHTML = “ width=“800″ height=“450″>

#17 V dalším kroku zkusíme už TrustedHTML objekt vyžadovat, jinak zápis nedovolíme, to si zkuste na další demo stránce – všiměte si hlavičky CSP bez report-only a vytváření politiky pomocí trustedTypes.createPolicy a její následné použití pro zápis do innerHTML.

Escapování nechávají Trusted Types na vás, můžete použít DOMPurify, Sanitizer nebo obyčejný string.replaceAll(). Jak dobré escapování, tak dobré Trusted Types. Když se pokusíte do sinku zapsat string (tlačítko Enter any HTML to také zkusí), tak se to nepovede a pošle se report. Pokud by se vám nechtělo vytvářet objekty TrustedHTML ručně, tak můžete vytvořit escapovací politiku s názvem defaulttrustedTypes.createPolicy('default', …) a ta se pak použije vždy při zápisu do sinků, v tomto případě ale escapujte fakt dost solidně. Používání defaultní policy je k vidění na další stránce.

Tahle automatizace a prokazatelnost se mi moc líbí a těším se, až to bude ještě víc použitelný. V současný době spousta knihoven do sinků zapisuje, takže Trusted Types se často nedají provozovat ani v tom „report-only“ režimu. Možná jste si všimli, že JavaScript na těch mých Trusted Type demo stránkách není obarven, už je, knihovnu highlight.js jsem nahradil naivním obarvováním řetězců na serveru. Highlight.js totiž zapisuje do innerHTML a i jen při načtení té stránky to vygenerovalo dva reporty. Ale snad to vývojáři nějak brzo vyřeší.

Komentáře

Subscribe
Upozornit na
guest
0 Komentářů
Inline Feedbacks
View all comments

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.