2005-01-25

Älä luota selaimeen

Kolmas tyypillinen web-sovelluksista ja sivustojen dynaamisista osista löytyvä tietoturvaongelma on, että selaimen oletetaan toimivan määritetysten mukaan. Esimerkkejä:

- Lomakkeen kenttien arvojen pituuden rajoittaminen pelkästään maxlength-attribuutilla. Saattaa sallia tietokannan täyttämisen roskadatalla tai puskurin ylivuoden aiheuttamisen.

- Sen olettaminen, että select-elementistä muodostettu parametri vastaa tosiaan jotain option-elementtiä.

- Piilotettuina lomakekenttinä (type="hidden") välitettyjen arvojen käyttäminen ilman tarkistuksia. Näiden pitää valvoa olevan sallituissa rajoissa jokaisen kyselyn yhteydessä, muuten saattaa olla mahdollista esimerkiksi päästä käsiksi toisten tietoihin.

- Salassa pidettävien arvojen välittäminen piilotettuina lomakekenttinä.

- Kyselyn mukana välitettyjen otsakkeiden totuudenmukaisuuteen tai olemassaoloon luottaminen (esim. Referer [sic] ja X-Forwarded-For). Edelliseen sijaintiin perustuvan pääsyntarkistuksen tulee käyttää istuntokohtaisia tunnisteita eikä Referer-otsaketta. IP-osoitteisiin pohjautuvan pääsyntarkistuksen tulee käyttää kyselyn IP-osoitetta eikä X-Forwarded-For-otsakkeen osoitetta.

- JavaScriptin käyttäminen lomakkeen arvojen tarkistukseen ilman vastaavia palvelinpuolen tarkistuksia.

Näistä käytännöistä seuraa ongelmia seuraavista syistä:

1. Selain saattaa toimia väärin, joko siksi ettei se tue kyseistä osaa määrityksistä, koska siinä on ohjelmointivirhe tai koska se on muunnettu toimimaan eri tavoin.

2. Selain ei ole välttämätön palvelimen kanssa keskustelemiseen. Hyökkääjä pystyy tekemään täsmälleen sellaisen kyselyn kuin haluaa ja analysoimaan vastausta täsmälleen niin tarkkaan kuin haluaa.

Yhteyden salaaminen TLS:llä tai SSL:llä ei auta lainkaan mainitsemiini ongelmiin, minkään niistä hyödyntäminen ei edellytä toisen henkilön istunnon vakoilua.

Siispä, vastaus kysymykseen "onko tuo turvallinen" ei saa koskaan olla "selain huolehtii siitä". Tietoturvan näkökulmasta selainta ei joko ole olemassa tai se ei ainakaan ole ystäväsi.

(Edit: korjattu auki unohtunut elementti. Edit edit: korjattu auki unohtunut elementti editistä.)

Väsyttää

Että sitten osaa väsyttää. Yön unet jäivät neljään tuntiin, kiitos väsymättömän mutta valitettavasti tuntemattomaksi jääneen kitaristin. Tässä taas hieman makua siitä, millaista on asua TOAS:in Lapinkaari-asuntokohteessa:

Student Life at Lapinkaari

Mukava ja opiskelulle suotuisa ympäristö. Jes.

2005-01-18

Hämmästyttävä

Löysin Mezzoblue-sivustolta linkin Map.search.ch -sivustolle, eli Sveitsin karttahakuun. Hämmästyttävä toteutus. Sellainen varaus tosin, että vaikka täältä yliopiston verkosta vasteajat ovat olemattomia, modeemikäyttäjänä ollessani pitäisin kuitenkin varmaan enemmän jostain Eniron karttahaun tyyppisestä palvelusta (jota muuten säännöllisesti käytän).

Safari-selaimen käyttäjänä minua puolestaan hemmotellaan Elements or Lower -sivustolla todella tyylikkäällä leipätekstillä.

2005-01-17

Käytä mahdollisimman vähän parametreja

Toinen tyypillinen web-sovelluksista löytyvä tietoturvaongelma ja tyylirikko on tarpeettomien parametrien välittäminen.

Ugly Bench

Oletetaan, että meillä on jonkinlainen webissä toimiva keskustelujärjestelmä. Jokaisella keskustelualueella on numeromuotoinen tunniste, samoin kuin jokaisella ketjulla. Kahdella ketjulla, vaikka ne olisivatkin eri alueilla, ei ole samaa tunnistetta. Jokainen ketju on vain yhdellä alueella.

Ketjuun voi nyt viitata kahdella tavalla: viewthread.php?bid=12&tid=234 tai viewthread.php?tid=234. Väitän, että näistä jälkimmäinen, lyhempi, tapa on lähes aina parempi. Ensimmäistä tapaa käytettäessähän on mahdollista pyytää sivua viewthread.php?bid=11&tid=234, mistä aiheutuvat seuraavat kaksi ongelmaa:

1. Pääsyntarkistus jää helposti puolitiehen, eli se tapahtuu pelkästään bid-parametrissa mainitun keskustelualueen tunnisteen perusteella. Tällöin on ketjun tunnisteen tietämällä mahdollista lukea ketju alueelta, johon pääsyä ei oikeastaan pitäisi olla.

2. Vaikka kummankin parametrin sallittavuus tarkastettaisiinkin erikseen, kyselyn yhtenäisyyden tarkastaminen usein unohtuu, eli ketjun voi saada näyttämään eri alueella olevalta. Tämä voi olla pahakin ongelma jossain muussa järjestelmässä, esimerkiksi sellaisessa jossa parametrina on kuvan tunniste ja siihen liittyvän tekstin tunniste.

Lisäksi:

3. Jos ketju siirretään jollekin toiselle alueelle, eivät siihen aiemmin tehdyt linkit enää toimi.

Mikäli ketju voisi olla useammalla alueella kerrallaan, silloin saatetaan tarvita kahta parametria, jotta tiedetään kumman alueen kontekstissa ketju näytetään. En yritä väittää, että kahden parametrin käyttäminen yhden riittäessä automaattisesti tekisi järjestelmästä epäturvallisen. Yhtenäisyys tulee vain valvotuksi kuin itsestään, jos ketjun alueen hakee aina erikseen tietokannasta, eikä tietokantakyselyä pysty säästämään juuri tämän yhtenäisyyden valvomisen takia.

PS. Ohje pätee, vaikka parametrit välitettäisiinkin selaimen näkökulmasta osana polkua, esim. /11/234, ja vaikka kysely olisikin POST-metodia käyttäen tehty.

2005-01-12

Kuvattu

Me Again

Vietin joululoman kotona ja pikkuveljet innostuivat harvinaisen paljon digijärkkäristä. Yllä yksi tuotos.

2005-01-11

Päivämäärävälien esittämisestä

Oletko koskaan nähnyt kenenkään ihmisen kirjoittavan esimerkiksi 3.1.2005-5.1.2005? En minäkään, vaan jokainen tuntemani ihminen kirjoittaisi tuon 3.-5.1.2005. Jostain syystä tällainen päivämääränvälin esitysmuoto on kuitenkin tavallinen web-sovelluksissa.

Jonkinasteisena perfektionistina tein jokin aika sitten tekemääni päivämäärävälin esittävään rutiiniin vielä pienen parannuksen, eli väliviivan sijasta käytetään rimpsua  –  eli 3. – 5.1.2005. Tasapainoista ja helppolukuista. Valitettavasti IE ei tajua sellaista typografista hienoutta kuin ohutta välilyöntiä, eli IE:lle pitää näyttää tavallinen versio.

Muita päivämääriin liittyviä asioita:

- Aina kun käytät kuukauden nimeä, liitä yhteyteen myös kuukauden numero. Jotkut aikuisetkin joutuvat muuten laskemaan kuukausia.

- Virheiden välttämiseksi kannattaa harkita viikonpäivän näyttämistä jokaisen tai useimpien päivämäärien yhteydessä.

- Älä oleta ihmisten muistavan viikkonumeroita. Näytä viikkonumeron yhteydessä päivämääriväli ja salli viikon valitseminen viikolle sijoittuvan päivän syöttämisellä viikkonumeron sijaan.

- Jos vuosi on selvä käyttöyhteydestä, käytä pelkästään päivää ja kuukautta, esim. 5.1. . Tästä pidemmälle supistaminen tuskin kannattaa, sillä lukua ei enää silloin tunnista päivämääräksi.

- Harkitse jonkin suhteellisen ajanmääreen (esim. tänään, huomenna, eilen sekä mahdollisesti myös toissapäivänä ja ylihuomenna) käyttämistä päivämäärän sijaan. Esimerkkejä tästä löytyy useista mailiohjelmista.

2005-01-07

Miksi kaikki web-sivulle liitettävä teksti tulee siistiä HTML:ästä

Re: SQL Injection Attacks by Example ja Kaistan varastamista, tietokantaan tunkeutumista ja salaisuuksien vuotamista.

Visa Kopun blogista huomaamassani jutussa viitattiin artikkelin SQL Injection -hyökkäyksistä. Hieman yksinkertaistaen käy niin, että palvelimella pyörivän ohjelman koodissa muodostetaan SQL-lause ja tämän lauseen osana käytetään selaimelta parametrina tulevaa merkkijonoa ilman asiaankuuluvia tarkistuksia. Näin hyökkääjä voi vääntää sinänsä viattoman lauseen tietomurron tai vahingonteon työkaluksi.

Tämä muistutti minua taas siitä, miten huolettomia monet ohjelmoijat ovat myös ohjelman toisessa päässä, eli tietokannasta tulevan tiedon yhdistämisessä osaksi web-sivua. Onhan nimittäin niin, että ellei datan ole erityisesti tarkoitus olla HTML:ää, tulee se aina siivota vähintäänkin muuntamalla pienempi kuin (<), suurempi kuin (>), et-merkki (&) ja lainausmerkit (") vastaaviksi koodauksiksi (&lt;, &gt;, &amp; &quot;).

Mikäli tätä varotoimea ei noudateta, voivat seuraukset olla vakavia. Sovelluksen käyttäjätunnuksia on mahdollista kaapata, kaistanleveyttä tuhlata tarkoituksella, kävijöiden koneella voi ujuttaa haittaohjelmia selainten (eli siis lähinnä IE:n) tietoturva-aukkoja hyödyntäen ja ehkä kaikkein uskomattominta, tehdä erinäisiä hyökkäyksiä myös muita sivustoja vastaan. Eikä tässä vielä lähellekään kaikki.

Näistä esimerkeistä haittaohjelmien ujuttaminen vaatii selainbugeja toimiakseen. Muut toimivat, vaikka selainohjelma toimisikin täydellisesti. Käyttäjätunnuksien kaappaaminen ilman haittaohjelmaa vaatii JavaScriptin tai VBScriptin lisäämistä sivulle (mm. Cross Site Scripting eli XSS).

Ei luulisi, että vaikkapa PHP:stä aivan vakiokirjastosta löytyvän funktion (muistaakseni htmlspecialchars) käyttö on niin vaikeaa. Kokosin tähän muutamia foorumeilta ja koodin kommenteista lukemiani perusteluja vastaväitteineen:

Rajoitan tekstin pituuden hyvin lyhyeksi, jolloin mikään skripti ei mene läpi. Oletetaan, että sallittu tekstin pituus on 50 merkkiä. Tähän tilaan mahtuu mm. seuraava hyökkäys: <script src=http://paha.exp language=VBScript>. En ole tutkinut asiaa tarkemmin, mahdollisesti tästäkin voisi vielä pudottaa jotain. En tiedä, voiko script-elementtiä vaativaa hyökkäystä toteuttaa 25 merkissä, mutta harvemmin 25 merkkiä riittää mihinkään oikeaan käyttöön. Suomen kielessähän jo yksi yhdyssana voi olla reilusti pidempi. Lisäksi kuvallakin saa pahaa aikaan ja sellaisen lisää lyhemmässä tilassa ja joskus useampi kenttä näytetään vierekkäin, vaikkapa etu- ja sukunimi, jollain käytettävissä oleva tila tuplaantuu.

Käytän rutiinia, joka poistaa ikävien elementtien alut. Teoriassa riittävää, mutta kyseinen rutiini jää usein helposti kierrettäväksi. Tyypillisiä ongelmia:

- rutiini korvaa vain isoilla kirjoitetut alut (<SCRIPT) ja pienellä kirjoitetut alut (<script), mutta ei sekaisin kirjoitettua alkua (esim. <SCripT)

- rutiini käy tekstin läpi vain kertaalleen, eli syötteestä <sc<scriptript tulee <script

- rutiini käy etsittävät merkkijonot läpi jossain tietyssä järjestyksessä, esimerkiksi script-elementit ennen img-elementtejä, jolloin syöte <sc<imgript tuottaa tulokseksi <script

- kaikkia vaarallisia elementtejä ei muisteta etsiä, esimerkiksi form-elementin avulla on kätevä rakentaa väärään paikkaan osoittava näköislomake

Käytän rutiinia, joka poistaa ikävät elementit kokonaan. Teoriassa riittävää, mutta taaskin kattava toteutus osoittautuu vaikeaksi. Tagien katkominen toisilla tuhoaa monet regexit (lauseet, joilla ilmaistaan etsittävän tekstin säännöt) ja usein tekstiä verrataan ryhmään regexejä jossain järjestyksessä. Oma jäsenninkään ei ole välttämättä yhtään turvallisempi, koska se ei kumminkaan vastaa lähellekään selaimen jäsennintä (ellei kyseessä ole XML).

Kirjoittamani ohjelmisto ei ole avointa ohjelmistoa, joten kukaan ei voi keksiä tietä heikkojen suojausteni ohi. Todennäköisesti jokainen virhe on jo tehty jossain muualla, eli jokin versio kasasta koottuja vakiohyökkäyksiä voi toimia. Lisäksi näiden tuottamien tulosten seuranta ja päättely tältä pohjalta tuo helposti tuloksia.

Poistan pelkästään suurempi kuin - ja pienempi kuin -merkit, sillä muilla ei ole väliä. Ajatellaan vaikkapa linkkilistaa, jossa jokainen linkki on muotoa <a href="$url">$otsikko</a> ja paikanpitäjän $url korvaavaa tekstiä ei tarkasteta mitenkään. Tällöin kätevä hyökkäys on " onclick="...scriptiä....

Ainoastaan luotettavat henkilöt tulevat käyttämään sovelluksen ylläpitopuolta. Vaikka henkilöt olisivatkin varmasti hyväntahtoisia, saattaa heidät pystyä huijastaan lisäämään sivustolle haitallista koodia. Lisäksi mikäli yhdenkin tunnukset (tai miten tunnistaminen nyt yleensäkin tapahtuu) kaapataan, on muihinkin mahdollista päästä käsiksi.

Kaikki vahinko rajoittuu omaan sivustooni (tai sovellukseeni), eikä sillä ole sen kummempaa väliä. Selainbugeja käyttäen hyökkääjä saattaa pystyä kaappaamaan kokonaan vierailijan tietokoneen. Tai mikäli hyökkääjä lisää puolen megan kuvan joltain kolmannelta sivustolta, 1x1 pikselin kokoisena tietenkin, jokaiselle oman sivustosi sivulle, saattaa kolmannen sivuston ylläpitäjää odottaa ikävä kaistamaksu.

En väitä, ettei tarvetta HTML:än syöttämiseen web-sovellukseen tulisi koskaan. Nykyisinkin vain muista käyttöympäristöistä tai uusmedia-alalta tulleet ohjelmoijat eivät tunnu lainkaan ymmärtävän eroa kuvauskielen (markup language) ja puhtaan tekstin (plain text) välillä. Ja niille, joiden mielestä homma tuntuu sekavalta jo tässä vaiheessa, voin kertoa että selainten HTML-jäsentimien dokumentoimattomissa ominaisuuksissa ja Unicode-merkistön tuessa on vielä paljon mutkistavia asioita. Siispä:

1. Pitäydy kaiken sivulle tietokannasta tulevan käyttämisessä PHP:n htmlspecialchars:ia vastaavan rutiinin läpi niin kauan kuin mahdollista.

2. Jos joudut antamaan käyttäjille tehokkaammat muotoilumahdollisuudet joissakin kentissä, harkitse BBCoden, MarkDownin, WikiTextin, Textilen tai vastaavan muotoilumenetelmän käyttöä HTML:än sijaan. Ole näidenkin kanssa varovainen.

3. Mikäli joudut käyttämään tai ehdottomasti haluat käyttää HTML:ää, tee HTML:n puhdistusrutiini, joka päästää lävitseen vain oikeaksi tiedettyä mallia muistuttavaa koodia eikä mitään tuntematonta.