- COMP.SEC.100
- 18. Turvallinen ohjelmistokehitys (SDL)
- 18.1 Turvallinen ohjelmistokehitys
Turvallinen ohjelmistokehitys¶
Tässä osuudessa esitetään, miten toteutetaan ohjelmistokehitystä turvallisuus huomioiden. Näkökulma on erityisesti ohjelmistokehityksen prosesseissa.
Ohjelmiston reaktiivinen suojaus tarkoittaa sitä että, virheitä korjataan vasta sitten, kun ne havaitaan. Tässä on kuitenkin haittapuolensa:
- Tämä maksaa paljon. Poneman institituutin 477 yritykseen vuonna 2018 tehdyn tutkimuksen mukaan kustannukset ovat mittavat: Yhden tietomurron kustannus oli Yhdysvalloissa 7,9 miljoonaa dollaria, 5.3 miljoonaa lähi-idässä ja pienimmillään Brasiliassa, 1.2 miljoonaa. Lisäksi murrosta aiheutuu vaikeasti mitattavaa mainehaittaa.
- Hyökkääjät voivat jatkaa pitkään huomaamattomasti. Poneman instituutin tutkimuksen mukaan haavoittuvuuden hyväksikäytön havaitseminen kestää 197 päivää ja korjaus kestää 69 päivää (mediaani).
- Korjaukset voivat saada aikaan uusia haavoittuvuuksia.
- Korjausten asentaminen myös kestää.
- Päivityksiä ei usein asenneta. Esimerkiksi Heartbleed-haavoittuvuus havaittiin vuonna 2014, mutta vuonna 2017 oli edelleen 200 000 haavoittuvaa laitetta verkossa.
Microsoftia kritisoitiin lukuisista virheistä ja ohjelmistoa julistettiin jopa käyttökieltoon. Vuonna 2003 Microsoft teki ”Trustworthy computing” -julistuksen, jonka tavoitteena oli parantaa ohjelmistoturvallisuutta. Julistus laitettiin käytäntöön parantamalla ohjelmistokehitysprosessia ja näin muodostui vähitellen Microsoftin kehittämä turvallinen ohelmistokehitysmalli SDL (Secure Software Delvelopment Lifecycle), jonka periaatteet Microsoft julkaisi. Turvallisesta ohjelmistokehitysmallista käytetään myös lyhenteitä SSDL ja SDLC. Microsoftin SDL on kehitetty suuren yrityksen näkökulmasta, mutta sen periaatteita voi soveltaa myös pienimuotoisempaan ohjelmistokehitykseen. Microsoft on julkaissut mallin myös PK-yrityksille.
Tyypillistä SDL:lle on, että siinä huomioidaan turvallisuus alusta alkaen osana ohjelmistokehitystä: mm. määritetään kehitettävän ohjelmiston tietoturvatavoitteita, otetaan turvallisuutta parantavia työkaluja pakollisena käyttöön sekä asetetaan ohjelmistokehitykseen osallistuville henkilöille rooleja, jotka edistävät turvallisuutta. Esimerkiksi roolina voi olla huolehtia, että asetetut turvallisuustavoitteet täyttyvät.
Microsoftin SDL sisältää mm. seuraavia osia:
- Koulutus. Koulutus on tärkeä tapa edistää osaamista, jotta ohjelmistokehittäjät tietävät oikeat toimintatavat ja oppivat tekniset yksityiskohdat, joilla parannetaan ohjelmistojen turvallisuutta.
- Tietoturvamääritysten luominen. Tietoturvamääritykset ovat tärkeitä, koska niiden avulla ohjataan turvallista ohjelmistokehitystä ja voidaan määrittää ohjelman turvallisuus. Yhtenä esimerkkinä tietoturvamäärityksestä voi olla, että ohjelmassa ei saa olla yhtään SQL-haavoittuvuutta. Tämä voidaan sitten todentaa mm. testaamalla, koodikatselmoinnilla, ja staattisella analyysilla. Toisena esimerkkinä tietoturvamäärityksestä on, että käytetään määrättyjä staattisia analysaattoreita tietyissä vaiheissa ohjelmistokehitystä eikä staattinen analyysi saa ilmaista yhtään haavoittuvuutta. Tietoturvamäärityksiä pitää huomata päivittää, koska uhat myös muuttuvat. Tietoturvamääritysten luomiseen on kehitetty valmiita prosesseja, kuten SQUARE (Security Quality Requirements Engineering).
- Metriikat. Tietoturvallisuuden tason määrittäminen ja päätösten tekeminen edellyttää käytännössä metriikoita. Metriikoiden avulla määritetään pienin mahdollinen hyväksyttävä tietoturvallisuuden taso. Yksi metriikka on KPI (Key Performance Indicator), jota käytetään johdon päätöksenteossa. KPI:t kuvaavat ja mittaavat organisaation liiketoimintaa, mutta ne voidaan sovittaa myös ohjelmistokehityksen tietoturvatavoitteisiin kuvaamaan ohjelmiston turvallisuustasoa. Tätä voidaan käyttää esimerkiksi viestinnässä organisaation johdolle. Esimerkkejä tällaisista KPI-mittareista on ratkaisemattomien haavoittuvuuksien lukumäärä, haavoittuvuuden korjaukseen kuluva keskimääräinen aika ja haavoittuvuuksien muodostumisen odotettu lukumäärä ohjelmoitaessa tietty määrä koodia. Lisäksi haavoittuvuuksille voidaan tehdä riskiarvio, mikä auttaa mm. organisaation johtoa hahmottamaan kokonaistilannetta.
- Lainsäädännön noudattaminen On tärkeää huomioida, että ohjelmisto noudattaa lainsäädännön vaatimuksia. Erityisesti GDPR ja PCI DSS (Payment Card Industry Data Security Standard) ovat kansainvälisesti merkittäviä. EU:n tietosuoja-asetuksen noudattamisesta on tarkemmin kohdassa EU:n tietosuoja-asetuksen vaatimukset ohjelmistoille.
- Uhkamallinnus. Uhkamallinnus on tärkeää myös ohjelmistokehityksessä. Tällöin otetaan uhkamallinnukseen ohjelmiston ja sen käyttämän tiedon näkökulmat. Yksi malli on STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege). Lyhennelmän termit kuvaavat myös uhkamallinnuksen sisältöä: Tekeytyminen toiseksi, peukalointi, kiistäminen, tiedon vuotaminen, palvelunesto ja oikeuksien luvaton nosto. Uhkamallinnuksesta on tarkemmin riskienhallinnan yhteydessä.
- Suunnitteluperiaatteiden muodostaminen. Jotta ohjelmistokehitys olisi kustannustehokasta, kannattaa panostaa turvallisuuteen jo suunnitteluvaiheessa.
Saltzer ja Schroeder ehdottivat jo vuonna 1975 keskeiset ja muuttumattomat periaatteet:
- Mekanismin taloudellisuus (Economy of mechanism) eli järjestelmän pitäminen mahdollisimman yksinkertaisena ja pienenä. Tällöin myös virheet minimoituvat ja käyttöliittymä pysyy ymmärrettävänä.
- Turvalliset oletukset (Fail-safe defaults) eli oletusarvojen, kuten pääsynvalvonnan tai vaikka muuttujien alustuksen pitää olla turvallinen. Näin turvallisuus on automaattisesti käytössä.
- Turvallinen siirtyminen (Complete mediation) eli kaikkien objektien auktorisointi pitää tarkistaa, kun tietoa siirtyy. Tällä estetään väärien käyttöoikeuksien aiheuttamia haavoittuvuuksia.
- Avoin suunnittelu (Open design) eli turvallisuuden pitää perustua avoimeen suunnitteluun, ei ratkaisujen kätkemiseen. Tästä hyvänä esimerkkinä on kryptografiset algoritmit, jotka ovat kaikille avoimia. Avoimuudesta seuraa, että mahdolliset puutteet tulevat julkisen kritiikin kohteeksi, jolloin myös turvallisuus paranee ja tulee suljettua järjestämää paremmin todennettua.
- Vähimmät yhteiset toiminnot (Least common mechanisms) eli käyttäjien kesken jaettuja resursseja (muistia, tiedostoja jne.) on oltava mahdollisimman vähän eli ainoastaan todellisen tarpeen mukaan. Tällä vältetään resurssien yhteisessä käytössä olevia ongelmia.
- Psykologinen hyväksyttävyys (Psychological acceptability) eli käyttöliittymä tulee suunnitella helppokäyttöiseksi, jotta käyttäjät osaavat tehdä turvalliset valinnat automaattisesti, katso luku inhimilliset tekijät.
Näiden lisäksi on kaksi tärkeää periaatetta:
- Puolustuksen syvyys (Defense in depth) eli useiden turvakerrosten rakentaminen. Yhden suojauksen pettäessä on vielä muita turvakerroksia jäljellä, jotka hyökkääjän pitää ohittaa.
- Varautuminen päivityksiin (Design for updating) eli ohjelmiston rakentaminen niin, että muutokset ovat mahdollisia. Ohjelmiston päivittämisen tulisi olla mahdollisimman turvallista ja vaivatonta.
Turvallinen ohjelmointi edellyttää myös tietoturvaratkaisujen valintaa. Esimerkiksi kryptografian toteutuksesta pitää tarkistaa turvalliset valinnat, samoin autentikoinnista ja lokitietojen keruusta.
Määrittele ja käytä kryprostandardeja. Krypton käyttäminen on erittäin tärkeää, jotta luottamuksellisuus, eheys ja yksityisyydensuoja voidaan toteuttaa. Onkin tärkeää käyttää resursseja siihen, että krypto tulee oikein valittua ja toteutettua. Vakavia virheitä tuleekin tehtyä perehtymättömyyden vuoksi. Esimerkiksi valitaan turvattomia krypto-algoritmeja, niiden moodeja tai pahimmillaan jätetään suojausta vaativa tieto kokonaan salaamatta. Onkin tärkeää perehtyä turvallisiin krypto-standardeihin ja toteuttaa krypto niiden mukaisesti. Tässä on hyvä pitää mielessä yksi tärkeä turvallisen ohjelmoinnin periaate: ”Pyörää ei kannata keksiä uudestaan.” Kryptossa tämä tarkoittaa parhaiden toimintatapojen selvittämistä ja ottamista käyttöön sekä niiden oikeaa toteuttamista. Ts. ohjelmistokehittäjän tehtävä on hyväksi todetun kryptostandardin toteuttaminen ohjelmistoon. Kryptossa ohjelmistokehityksessä on tärkeää myös muistaa varautuminen tulevaisuuteen: Krypton vaihtamisen pitäisi olla mahdollista ja helposti toteutettavissa.
Määrittele ja toteuta kolmansien osapuolien ohjelmistokomponenttien riskien hallinta. Ohjelmasta on riskialtista viitata ulkoisten osapuolien ohjelmistokomponentteihin. Näin joudutaan kuitenkin käytännössä tekemään, sillä kaikkea ei kannata toteuttaa itse. Tyypillistä on esimerkiksi ulkoisten ohjelmarajapintojen kutsuminen sekä ohjelmakirjastojen käyttö. Riskejä voidaan hallita säännöllisesti seuraamalla ulkopuolisten komponenttien haavoittuvuuksia. Haavoittuvuuksien korjaamiseksi pitää olla valmis suunnitelma ja ohjelma pitää pystyä tarvittaessa päivittämään. Riskiä voidaan pienentää myös varautumalla ennalta ulkoisen ohjelmistokomponentin antamaan arvaamattomaan syötteeseen. Ulkopuolisten kirjaston tai ohjelmiston valinnassa kannattaa huomioida onko kirjasto tai ohjelmisto laajasti käytössä ja päivitetäänkö sitä edelleen. Esimerkiksi GitHubista löytyy sinänsä hyödyllisiä kirjastoja, mutta ne on merkitty arkistoiduksi, jolloin niitä ei enää päivitetä.
Käytä hyväksyttyjä työkaluja. Organisaation tulee määrittää hyväksytyt työkalut ja niiden asetukset sekä käyttö osana ohjelmistokehitystä. Esimerkiksi määritetään, mitä kääntäjiä on sallittu käyttää sekä määritetään asetukset, joilla kääntäjiä ajetaan.
Tee staattista analyysia. Staattisen analyysin työkalut ja koodikatselmoinnit ovat tärkeä osa ohjelmistokehitystä. Organisaatiossa nimetään hyväksytyt staattiset analysaattorit sekä niiden asetukset ja määritetään vaatimukset, jotka ohjelman on täytettävä, jotta se voidaan hyväksyä ohjelmistokehityksen eri vaiheissa. Näitä päivitetään tarvittaessa.
Tee dynaamista analyysia. DASTia (Dynamic Analysis Security Testing) tehdään ohjelman ajon aikana testaamaan ohjelman toimintaa ja etsimään virheitä ja haavoittuvuuksia. DAST sisältää usein ennalta rakennettuja työkaluja ja hyökkäyksiä. Myös fuzz-testaus sisällytetään usein osaksi DASTia. Kuten SAST, DAST voidaan ottaa osaksi ohjelmistokehitysputkea. DAST voidaankin nähdä automatisoituna penetraatiotestauksena.
Tee penetraatiotestausta. Penetraatiotestausta tekee yleensä joko organisaation sisäinen ryhmä tai organisaation ulkoiset konsultit. Penetraatiotestauksen tavoitteena on löytää haavoittuvuuksia ja virheitä ohjelmistosta, jotta ne voidaan korjata. Laajasti käytettynä viitekehyksenä penetraatiotestaukselle on OWASPin TOP 10 -lista.
Toteuta prosessi tietoturvapoikkeamille. Kaikista varotoimista huolimatta on oletettavaa, että ohjelmiston haavoittuvuuksiin tai muihin tietoturvatapahtumiin joudutaan jossain vaiheessa reagoimaan. Siksi on tärkeää olla varautunut ennalta. Tämän vuoksi laaditaan valmiussuunnitelma (IPR, Incident Response Plan). IPR:n tulee sisältää, keneen tai mihin ollaan yhteydessä tietoturvapoikkeamissa ja hätätilanteissa, menettelyt haavoittuvuuksien ratkaisemiseksi ja vaikutusten vähentämiseksi, menettelyt asiakkaiden, käyttäjien ja viranomaisten kanssa viestimiseen. IPR:n tulee huomioida myös kolmannen osapuolen ohjelmistot ja komponentit, joihin on riippuvuuksia. IPR tulee myös testata ja sitä tulee kehittää havaintojen perusteella.