Skip to content
Language:hu Author:Laborci Gergely
web-security

XSS: amikor az adat kóddá válik

Célok

A lecke végére el tudod magyarázni az XSS lényegét, meg tudod különböztetni a tárolt, a visszatükrözött és a DOM-alapú változatot, és ismered a védelem rétegeit: kimeneti kódolás, szöveg- és HTML-API-k, tisztítás, Content Security Policy.

Miért veszélyes egy hozzászólás?

A Kurzuspontban a hallgatók hozzászólást fűzhetnek egy kurzushoz. Ha az alkalmazás a szöveget ellenőrizetlenül HTML-ként építi be az oldalba, a böngésző a támadó által megadott részt kódként értelmezi. Az MDN szerint az XSS lehetővé teszi a támadónak, hogy kódot futtasson a sérülékeny oldal saját eredetében, ahol a böngésző úgy kezeli, mintha az oldal szerzője írta volna: olvashatja az oldalt és a helyi tárhelyet, és a felhasználó hitelesítő adataival kérhet a szervertől. A sérülés oka mindig az, hogy a szöveg és a futtatható tartalom határa elveszett.

Három változat

Változat Hol kerül a rosszindulatú tartalom az oldalba? Példa
Tárolt (stored) A szerver eltárolja, és később másoknak is megjeleníti. Hozzászólás, amit minden látogató lát.
Visszatükrözött (reflected) A kérésből (például URL-paraméterből) vett adat kerül a válaszba. Keresőoldal, amely kiírja a keresőszót.
DOM-alapú (DOM-based) A kliensoldali kód ír veszélyes módon adatot a DOM-ba. innerHTML fed a location.search-ből.
flowchart LR
    A[Támadó által adott szöveg] --> S[Alkalmazás]
    S -->|veszélyes HTML-beillesztés| B[Áldozat böngészője]
    B -->|kód fut a sérülékeny eredetben| R[Bejelentkezett művelet]

Kipróbálva: kimeneti kódolás nélkül és azzal

A hozzászólás szövege egy támadó üzenete. Egy egyszerű szerveroldali sablon (JavaScript sablonszöveg) két módon építi a HTML-t:

const komment = '<img src=x onerror="alert(document.cookie)">';
const veszelyes   = c => `<p class="komment">${c}</p>`;
const esc = s => s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
                  .replace(/"/g,'&quot;').replace(/'/g,'&#39;');
const biztonsagos = c => `<p class="komment">${esc(c)}</p>`;
veszélyes   : <p class="komment"><img src=x onerror="alert(document.cookie)"></p>
biztonságos : <p class="komment">&lt;img src=x onerror=&quot;alert(document.cookie)&quot;&gt;</p>

Az első kimenetben egy img elem van, amelynek hibakezelője (onerror) kódot futtat: a böngésző ezt az oldal részeként értelmezi. A második kimenetben a <, > és " karakterek HTML-entitássá alakultak, ezért a böngésző szövegként jeleníti meg, nem elemként.

A kimeneti kódolás (output encoding) kontextusfüggő: más szabályok kellenek a HTML-szövegben, egy attribútumban vagy egy JavaScript-ben. Attribútumban egy idézőjel is kiléphet az értékből:

escape nélkül: <input value="" onmouseover="alert(1)">
escape-pel   : <input value="&quot; onmouseover=&quot;alert(1)">

Az MDN ezért azt tanácsolja, hogy kimeneti kódolást végző sablonmotort használjunk, tartsuk szem előtt a kontextust (attribútumoknál az értéket mindig idézőjelek közé tegyük), és a szkript- és stíluskörnyezeteket tekintsük szinte mindig veszélyesnek. A fenti esc függvény oktatási példa: valódi alkalmazásban bevált sablonmotort vagy könyvtárat kell használni, nem saját megoldást.

A védelem rétegei

  1. Kimeneti kódolás: a sablonmotor alapértelmezésben kódoljon, és a kontextust ismerjük.

  2. Szöveg-API-k a HTML-API-k helyett: ha csak szövegre van szükség, ne HTML-ként értelmezzük. Böngészőben az innerHTML HTML-ként értelmezi a szöveget, a textContent szövegként illeszti be:

    welcome.innerHTML   = `Üdv, ${nev}!`;   // HTML-ként értelmezve: veszélyes
    welcome.textContent = `Üdv, ${nev}!`;   // szövegként: biztonságos
    
  3. Tisztítás (sanitization): ha valóban HTML-t kell fogadni (formázott hozzászólás), ismert, jó hírű tisztító könyvtárat kell használni (az MDN a DOMPurify-t említi), saját szűrőt nem.

  4. Content Security Policy (CSP): a böngészőnek küldött szabályrendszer, amely korlátozza, honnan tölthet be szkriptet az oldal. Az MDN szerint elsődleges célja az XSS elleni védelem: akkor is megakadályozhatja az idegen szkript futtatását, ha a tisztítás elbukott.

    Content-Security-Policy: script-src 'nonce-{VÉLETLEN}'; object-src 'none'; base-uri 'none'
    

    A nonce egy válaszonként új véletlen érték, amely a fejlécben és az engedélyezett <script nonce="..."> elemekben egyezik; a beszúrt idegen szkript nem ismeri. A CSP letiltja a beágyazott eseménykezelőket (onclick=...), a javascript: címeket és az eval() jellegű futtatást is. A CSP nem pótolja a bemenet helyes kezelését, hanem mögöttes védelmi réteg.

  5. Trusted Types: az MDN szerint a CSP require-trusted-types-for 'script' direktívájával kikényszeríthető, hogy veszélyes helyekre (például innerHTML) csak egy szabályzaton átment érték kerülhessen. Ez a megoldás nem tisztító, hanem a tisztítás kikényszerítésére való keretrendszer, és a böngészőtámogatása korlátozott.

Mit tud a támadó, ha mégis bejut?

A támadó kódja a sérülékeny oldal eredetében fut, ezért a bejelentkezett felhasználó nevében kérhet a szervertől. A HttpOnly cookie-t (7.2) a szkript közvetlenül nem olvashatja, de ettől még a bejelentkezett böngésző nevében érzékeny műveleteket kezdeményezhet, és a localStorage tartalmához is hozzáférhet (7.3). Az XSS ráadásul a legtöbb CSRF-védelmet (8.4) is meg tudja kerülni, mert a kérés a saját eredetből indul. A HttpOnly tehát enyhít, de nem pótolja az XSS megelőzését.

Végigvezetett példa: hozzászólás a Kurzuspontban

Lépés Döntés
A hozzászólás szövegét a szerver eltárolja Nyers szövegként, változtatás nélkül (nem „tisztítva” tároljuk, hanem megjelenítéskor kódolunk).
Megjelenítéskor A sablonmotor kódol (HTML-kontextus); a kliens textContent-et használ.
Formázott hozzászólás kell Tisztító könyvtár a megengedett elemek listájával.
Tartalék CSP nonce-szal, idegen szkript tiltásával.
Süti HttpOnly; Secure; SameSite.

Egy támadó hozzászólása így szövegként jelenik meg, nem elemként, és ha mégis hiba csúszik be, a CSP megakadályozza a szkript futtatását.

Gyakori tévhitek

Állítás Pontosítás
„A HttpOnly minden XSS-kárt megakadályoz.” A cookie olvasását korlátozza; a sérült oldalon futó kód más műveletekre képes.
„Elég a <script> szöveget kiszűrni.” Sok más út van (eseménykezelő attribútumok, javascript: címek); a kontextusfüggő kódolás kell.
„A HTTPS véd az XSS ellen.” A szállítást védi, nem az alkalmazás hibáját (4.3).
„A CSP elég, ha van.” Mögöttes védelem; a helyes kimenetkezelést nem pótolja.
„A bemenetet kell tisztítani tároláskor.” A kódolás a kimenet kontextusától függ; megjelenítéskor kell alkalmazni.

Röviden

  • Az XSS-nél adatból kód lesz: a támadó szkriptje a sérülékeny oldal eredetében fut.
  • Változatok: tárolt, visszatükrözött, DOM-alapú.
  • Védelem: kontextusfüggő kimeneti kódolás, textContent, tisztító könyvtár HTML-hez, CSP tartalékként.
  • A HttpOnly enyhít, de nem véd meg az XSS kárától.

Ellenőrző kérdések

  1. Mi a különbség a tárolt és a visszatükrözött XSS között?
  2. Mi a különbség az innerHTML és a textContent között?
  3. Miért nem pótolja a CSP a kimeneti kódolást?
  4. Miért veszélyes a localStorage-ban tárolt token XSS esetén?

Tanulási források

Prerequisites (2)

  • HTML
  • DOM

Teaches (3)

  • XSS
  • kimeneti kódolás
  • Content Security Policy