Koodi joka tekee vain sen mitä pitää eikä mitään muuta?
Äskettäinen vakava tietoturvalöydös GitHubissa nostatti varmasti monien kulmakarvoja. Vaikka kyseessä ei ollut pahantahtoinen yritys hyökätä järjestelmää ja sen tietoja vastaan, koko kuviossa on monta mielenkiintoista ja huolestuttavaakin puolta.
Ensinnäkään moni ei varmaankaan ole oikeasti miettinyt, kuinka paljon avoimiin open source -komponentteihin oikeasti luotetaan. Tässä tapauksessa olisi ollut verrattain triviaalia saada ujutettua pahantahtoista koodia sopiviin projekteihin. Ehkä asia olisi huomattu, ehkä ei. Moni ei ole itse käynyt läpi käyttämäänsä avoimen lähdekoodin koodia, mutta voi toivoa että projektin kehittäjät kiinnittäisivät asiaan huomiota.
Toisekseen haavoittuvuuden käsittely (varsinaisesti Ruby on Railsissa) ei ehkä mennyt kaikkien taiteen sääntöjen mukaan. Selviönä ei voi pitää, että aina vakavatkaan ongelmat saataisiin oikeasti nopeasti korjattua.
Asiasta on kuitenkin nostettava esille sen perimmäinen ongelma, eli Ruby on Railsissa oleva haavoittuvuus. Se ei ole mitenkään uniikki, vaan samanlaisia ongelmia on nähty muissakin teknologioissa.
Entä kun parametreina onkin vihamielistä sisältöä
Teknisesti GitHubin tapauksessa kyse oli Ruby on Railsin ns. Mass Assignment -toiminnon väärinkäytöstä, jossa tietokantaa päivitetään automaattisesti käyttäjän lisäämien parametrien perusteella. Näin tapahtuu myös niiden kenttien osalta, joita ei ollut tarkoitettu päivitettäväksi.
Hyvin samantyyppinen ongelma todettiin vuonna 2008 Spring MVC -frameworkissa, tapaus tunnetaan nimellä Data Submission to Non-Editable Fields. Spring MVC:ssä todettiin pari vuotta sitten myös vakavampikin ongelma, jossa käyttäjän lähettämät request-parametrit käsiteltiin niin, että tuloksena palvelin lähti lataamaan taglibien koodia hyökkääjän antamasta HTTP-osoitteesta – uskomatonta mutta totta. Haavoittuvuus referensseineen on kuvattu CVE-2010-1622-dokumentissa.
Vastaavanlainen tapaus on Strutsin OGNL-lausekkeiden käsittely. Struts käsittelee käyttäjän antamia OGNL-muotoisia request-parametreja automaattisesti ja mahdollistaa esimerkiksi järjestelmäkomentojen ajamisen etänä palvelimella. Tekniikkaan liittyy lukuisa määrä vakavia haavoittuvuuksia (vanhimmasta uusimpaan CVE-2007-4556, CVE-2008-6504, CVE-2010-1870, CVE-2012-0391 ja CVE-2012-0838). Nämä haavoittuvuudet ovat siis olemassa vaikkei kyseistä toimintoa varsinaisesti käyttäisikään.
Kaikki tapaukset ovat vakavia tietoturvaongelmia. Parhaiten niitä kuvaa seuraava Dr Dobb’s Journalissa esitetty kaavio (Thompson, Whittaker: ”Testing for Software Security”, Dr Dobb’s Journal November 2002):
Pääosin ohjelmisto tekee sen mitä sen on speksattu tekevän. Kuitenkaan kaikilta osin se mitä järjestelmän speksataan tekevän, ei välttämättä päädy toteutukseen. Tällöin syntyy juuri niitä tyypillisiä ohjelmistobugeja. Yllättävää kyllä, ohjelmisto toisaalta tekee asioita, joita sen ei ole tarkoitettu tekevän.
Nämä ovat hyviä kandidaatteja tietoturvaongelmien lähteiksi, ja juuri tällaisista asioista edellä mainituissa tapauksissa on kyse.
Mistä tällainen speksaamaton toteutus sitten syntyy? Tietenkään harva koodailee järjestelmään ylimääräisiä kiemuroita ilman mitään varsinaista tarvetta, oli se kirjattu spekseihin tai ei.
Käytännössä kyse on jostain seuraavista asioista:
-
Järjestelmän toimintalogiikkaa ei ole mietitty riittävän kattavasti, joten tietyillä syötteen arvoilla oikea toiminta on jätetty määrittämättä. Koska järjestelmän täytyy kuitenkin tehdä jotain syötteelle, ollaan vaarallisella vyöhykkeellä. “Hauskimpia” esimerkkejä ovat online-ostospalvelut, joissa voi esimerkiksi jättää palvelulle negatiivista tippiä. Tällöin ostoksen loppusumma pienenee sopivasti. Koska missään ei erikseen määritelty (tai mietitty) mitä negatiivisille arvoille tehdään, niin järjestelmä luonnollisesti laskee loppusumman ostosten hinnan ja tipin matemaattisena summana.
-
Järjestelmässä käytetään komponentteja, jotka helpottavat koodaajan työtä ja tekevät erilaisia asioita automaattisesti ja pyytämättä. Erilaiset automaattiset request-parametrien käsittelyt menevät juuri tähän kategoriaan. Kätevä toiminto, kyllä, mutta kovin vaarallinen. Jos miettii millainen määrä koodia oikeasti ajaa tyypillistä web-tietojärjestelmää, niin räätälöidyn ohjelmakoodin osuus on häviävän pieni verrattuna frameworkeihin, komponentteihin, sovelluspalvelimen koodiin ja muuhun ajettavaan koodiin, vaikka jättäisi käyttöjärjestelmätason kokonaan huomioimatta. Tämän huomaa helposti katsomalla virhetilanteiden yhteydessä näkyviä ns. stack traceja, joissa kutsupuun muutama taso on räätälöityä koodia ja 100 tasoa kaikenlaista muuta koodia.
-
Järjestelmässä on tuotantokäytön kannalta kuollutta koodia, esimerkiksi testaukseen ja vianselvitykseen käytettävää logiikkaa. Tällaista logiikkaa voi tietenkin olla myös muualla kuin räätälöidyssä koodissa, tyypillisesti vaikkapa frameworkin esimerkkikoodin muodossa.
Pahantahtoisen koodin ja takaporttien mahdollisuutta ei tietenkään voi sulkea pois, mutta käytännössä tällaiset tapaukset ovat kovin harvinaisia.
Mitä asialle sitten pitäisi tehdä?
Pohjimmiltaan pitäisi pystyä tarkasti määrittämään, mitkä ovat järjestelmän kannalta sallittuja ja odotettuja syötteitä (esim. URL-osoitteita, parametrien nimiä ja syötteiden arvoja). Kaikki muu pitäisi olla kiellettyä. Käytännössä tällaisen teknisen määrityksen muodostaminen, ylläpitäminen ja tekninen käyttö on hyvin hankalaa. Yhtenä vaihtoehtona on varmistaa, että käyttäjältä hyväksytään vain toimintoja ja syötteitä, jotka järjestelmä itse tarjoaa käyttäjälle. Parhaimmillaan tähän soveltuu käytetyn UI-frameworkin omat suojaukset (esim. linkkien kryptaus à la Wicket), mutta ulkoisiakin komponentteja voi sovitella paikalleen (esim. HDIV). Voi myös kerätä lokia järjestelmälle tulevista syötteistä ja parametreista ja sitten määrittää sallittu syötejoukko. Tietysti itse syötteiden arvoja harvoin pystyy kiinnittämään.
Jos ongelman onnistuu taklaamaan, samalla tulee usein hoitaneeksi OWASP:in Top 10 -listan ongelmat A4, A5 ja A8. Käänteisesti toimiminen ei kuitenkaan toimi eli Top 10 -listan huomioiminen ei suojaa tällaisilta ongelmilta.
Pahimpaan on kuitenkin syytä varautua ja rakentaa järjestelmän puolustus useamman kerroksen varaan ja hyviä periaatteita noudattaen. Toimiva komponenttihaavoittuvuuksien seuranta on myös keskeinen osa haavoittuvuuskartalla pysymistä.
Vielä lyhyesti alun GitHub/Ruby on Rails -ongelmaan. Hyviä yksirivisiäkin puolustustapoja sitä vastaan löytyy, kunhan niistä ollaan tietoisia ja velvoitetaan koodaajatkin seuraamaan valittua toimintatapaa. Tapa tulee kirjata kehittäjien Secure Coding Guidelinesiin tai vastaavaan käytännön ohjeeseen. Ja jos ohjeistusta ei vielä ole, sellainen on viimeistään nyt hyvä muodostaa ja ottaa käyttöön.
Lisää aiheesta: How Homakov hacked GitHub and the line of code that could have prevented it
1 kommentti




Tällaisen rakentamiseenkin toki voi jo mennä vuosi jos toinenkin, ja tulevaisuus näyttää, pysyykö tämä mielenkiintoinen mutta epäilemättä harvoin kustannustehokas koodinkatselmointityökalu mallissa mukana.
blogi