XSS (Cross-Site Scripting)

XSS-haavoittuvuuksien välttäminen

Helppo
15 min

XSS-haavoittuvuudet ovat melko yleisiä ja vakavia. Niilltä voi suurilta osin kuitenkin välttyä noudattamalla turvallisia kehityskäytänteitä, ja mikä parasta, web-sovelluksen voi koventaa CSP:llä (Content Security Policy) joka voi tehdä XSS-hyökkäyksistä lähes mahdottomia.

1. Suojaa sovellus CSP:llä

Content Security Policy (CSP, "sisällön suojauspolitiikka") on selaimen turvakontrolli jonka verkkosivut voivat vapaaehtoisesti ottaa suojakseen lähettämällä Content-Security-Policy -otsakkeen HTTP-vastauksissaan.

CSP:n perus toimintaperiaate on lisätä verkkosivun turvallisuutta rajoittamalla mitä sivustolla saa tapahtua ja mistä sivustolle saa ladata resursseja kuten skriptejä.

CSP on siis selainpuolen toteutus vähimmäisen oikeuden periaatteesta (principle of least privilege), eli annetaan sovellukselle vain välttämättömät oikeudet, jolloin hyökkäyksen sattuessa hyökkääjällä on mahdollisimman rajatut oikeudet aiheuttaa vahikoa.

Voit lukea CSP:stä lisää ja harjoitella sen käyttöä täällä.

2. Jos rakennat dynaamista HTML:ää, käytä siihen tarkoitettua templaattikirjastoa

Jos sinulla on perinteisen mallinen sovellus jossa HTML:ää rakennetaan lennosta ja lähetetään selaimeen, käytä HTML:n rakentamiseen soveltuvaa templaattikirjastoa kuten Jinja2 ja varmista että kirjasto osaa automaattisesti enkoodata parametrit oikein.

Älä myöskään käytä templaattikirjaston epäturvallisia funktioita jos sellaisia on, kuten esimerkiksi Jinja2 safe -avainsana (joka poistaa automaattisen enkoodauksen käytöstä).

3. Jos näytät HTML-sisältöä verkkosivustollasi, puhdista se ensin ja sijoita se hiekkalaatikkoon

DOMPurify

HTML:ää voi puhdistaa (eli poistaa siitä vaaralliset osat kuten skriptit) esimerkiksi DOMPurify -kirjastolla josta voit lukea enemmän täältä.

IFrame Sandbox

Lisäsuojana kannattaa vielä näyttää puhdistettu HTML sandboxatussa (eli hiekkalaatikon sisään rajatussa) kehyksessä josta voit lukea enemmän täältä.

Esimerkkitoteutus

function sandboxAndPurifyUntrustedHtml(untrustedHtml) {
    // Puhdistetaan luottamaton HTML DomPurifylla
    var purifiedHtml = DOMPurify.sanitize(untrustedHtml);
    
    // Luodaan hiekkalaatikko-iframe
    var iframe = document.createElement('iframe');
    iframe.sandbox = '';
    document.body.appendChild(iframe);
    iframe.src = 'data:text/html;charset=utf-8,' + encodeURIComponent(purifiedHtml);
}

4. Tarjoile kaikki lataukset asianmukaisella Content-Disposition-otsikolla estääksesi käyttäjän toimittaman HTML:n/SVG:n renderöinnin alkuperässäsi

Kun palvelimelta tarjoillaan tiedostoja (latauksia) käyttäjille, lähetä ne Content-Disposition-otsikolla, joka ilmoittaa että kyseessä on liite. Tällä tavoin tiedosto ei näy suoraan loppukäyttäjän selaimessa vaan latautuu tiedostona levylle, mikä estää XSS-haavoittuvuuden esimerkiksi sellaisessa tilanteessa jossa käyttäjä olisi saanut ladattua HTML- tai SVG-tiedoston sovellukseen jonka voi sitten ladata jostain URL-osoitteesta takaisin.

Content-Disposition: attachment; filename="document.pdf"

5. Varo dynaamisia URL-osoitteita linkeissä

On yleisesti tiedossa että URL-osoite saattaa olla muodossa https://, http:// tai alkaa vain kauttaviivalla jolloin URL osoittaa polkuun sovelluksen omassa osoitteessa.

<a href="https://www.hakatemia.fi">Normaali linkki</a>

On vähemmän tiedettyä että URL-osoite voi sisältää esimerkisi JavaScript-koodia joka suoritetaan kun linkki klikataan.

<a href="javascript:alert('xss')">XSS linkki</a>

Tästä syystä kannattaa näyttää linkit joiden HREF tulee sovelluksen ulkopuolelta turvallisella komponentilla joka osaa defensiivisesti näyttää vain https://, http:// tai kauttaviivalla (/) alkavat URL-osoitteet.

const SafeLink = ({ href, children }) => {
  const isValidLink = href.startsWith('https://') || href.startsWith('http://') || href.startsWith('/');

  if (isValidLink) {
    return <a href={href}>{children}</a>;
  } else {
    return <p>Invalid link</p>;
  }
};

6. Älä välitä luottamatonta dataa suoritettaviin JavaScript-funktioihin/ominaisuuksiin, kuten eval, setTimeout tai innerHTML

JavaScriptilla on mahdollista kutsua funktioita ja muuttaa HTML-elementtien attribuutteja jotka ottavat vastaan tekstiä (string) jotka suoritetaan suoraan koodina selaimessa.

On äärimmäisen tärkeää ettei mitään käyttäjän syötettä tai muuta ulkoisista lähteistä tulevaa tietoa anneta lainkaan tällaisille attribuuteille tai funktioille.

7. Käytä vain hyvämaineisia, paljon käytettyjä riippuvuuksia ja pidä ne ajan tasalla

Yksi tapa saada XSS-haavoittuvuus sovellukseen on käyttää JavaScript-kirjastoa jossa on XSS-haavoittuvuus.

dependabot

Jos käytät GitHubia, dependabotilla voit automaattisesti monitoroida käyttämiesi node-riippuvuuksien haavoittuvuuksia. Lue lisää Dependabotista täältä.

npm audit

Voit myös käyttää "npm audit" työkalua node-riippuvuuksien haavoittuvuuksien tarkastukseen. Lue lisää npm audit työkalusta täältä.

retire.js

Retire.js työkalu osaa löytää sovelluksesi käyttämät JavaScript-kirjastot ja etsiä niistä tunnettuja haavoittuvuuksia. Lue lisää retire.js -työkalusta täältä.

hakatemia pro

Valmis ryhtymään eettiseksi hakkeriksi?
Aloita jo tänään.

Hakatemian jäsenenä saat rajoittamattoman pääsyn Hakatemian moduuleihin, harjoituksiin ja työkaluihin, sekä pääset discord-kanavalle jossa voit pyytää apua sekä ohjaajilta että muilta Hakatemian jäseniltä.