Eristämiseen ja tietojen välittämiseen käytettävät menettelyt

Tässä luvussa tarkennetaan näkemystä keskeisiin käyttöjärjestelmän turvamekanismeihin. Useimpia jo pohjustettiin johdannossa. Tarkasteluun nostetaan (*=syventävästi)

  1. käyttäjien todennus, joskin lähinnä viitataan toiseen päälukuun.
  2. pääsynvalvonta: pääsylistat ja * kyvykkyydet.
  3. tiedon poistaminen muistista ja tallenteilta. Tämäkin pitää muistaa yhdistää käyttöjärjestelmien toimintaan mutta nostetaan esille vain tässä listassa. Asiaa on nimittäin käsitelty perusteellisesti “käänteisen” forensiikan yhteydessä.
  4. * muistin suojaus, ohjelmallisesti ja laitteiston avustamana.
  5. * laitteiston tarjoamat suojakehät. Otsikossa mainittu tietojen välitys tulee eniten esille tässä kohdassa, kun pitää siirtyä kehältä toiselle.
  6. kirjanpito, eli lokitus. Tämä on lähinnä viittaus aiempaan lukuun, ja kuten seuraavakin aihe, esillä vain tässä johdanto-osuudessa.
  7. hallinnointi, eli miten käyttöjärjestelmää itseään pitää käsitellä.
  8. viimeisessä alaluvussa hyvin lyhyesti sulautetut järjestelmät ja IoT-laitteet.

Kiinnitä huomiota siihen - koska sitä ole erikseen korostettu - miten vain etuoikeutettu koodi pääsee konfiguroimaan tarvittavan eristyksen ja takaamaan tarkistukset kaikille toiminnoille. Varsinaisen oppimistavoitteen ulkopuolelta tulee jonkin verran taustoitusta. Siitä voi olla hyötyä, koska moni asia oli iduillaan jo 1960-luvulla. Aloitetaan siis sieltä.

Multics oli 1960-luvulla ensimmäinen iso käyttöjärjestelmä, joka suunniteltiin alusta alkaen turvallisuutta ajatellen. Vaikka siitä ei koskaan tullut kovin suosittua, monet sen tietoturvainnovaatiot löytyvät edelleen suosituimmista käyttöjärjestelmistä. Vaikka jotkin ominaisuudet eivät olleet Multics-tiimin keksimiä, niiden integrointi yhteen, toimivaan, tietoturvalähtöiseen käyttöjärjestelmäsuunnitteluun oli silti uutta. Multics tarjosi suojakehiä, virtuaalimuistia, muistin segmenttipohjaista suojausta ja hierarkkista tiedostojärjestelmää, joka tukee sekä harkinnanvaraista että pakotettua pääsynvalvontaa (DAC ja MAC). USA:n armeijan pyynnöstä Multicsiin lisätty MAC oli pitkälti Bell-LaPadula-mallin ohjelmistototeutus. Lopuksi Multics varmisti, että sen monet pienet ohjelmistokomponentit olivat vahvasti kapseloituja, ja niihin oli pääsy vain niiden julkaistujen käyttöliittymien kautta.

Multics oli erittäin edistynyt, ehkä jopa ohi joidenkin nykyaikaisten käyttöjärjestelmien, mutta tämä oli myös sen ongelma. Järjestelmästä tuli niin suuri ja monimutkainen, että se loukkasi psykologisen hyväksyttävyyden periaatetta ainakin joidenkin sen kehittäjien osalta. Turhautuneena Ken Thomson ja Dennis Ritchie päättivät kirjoittaa uuden ja paljon yksinkertaisemman käyttöjärjestelmän. Sanaleikkinä he kutsuivat sitä “Unicsiksi”, joka myöhemmin kirjoitettiin Unix. Kuten kaikki yleisimmät nykyään käytössä olevat käyttöjärjestelmät, se luotti pieneen määrään primitiivejä suojausalueidensa eristämisessä. Unixiakin pienempi oli ja on opetuskäyttöön suunniteltu (Minix). Sen muuta käyttöä rajoittava lisenssi oli yksi syy nykyään hyvin yleisen Linuxin syntyyn (1991). Sen nimessä on sekä Linus Torvaldsia että Unixia, jonka kanssa sillä on paljon yhteistä varsinkin komentotulkin osalta.

Tämän luvun alaluvut käsittelevät enimmäkseen melko syvällä käyttöjärjestelmässä olevia turvajärjestelyjä. Ennen syventymistä niihin, tässä on luettelomainen katsaus aiheeseen kuuluvista mutta hieman lähempänä käyttäjää olevista asioista. Esimerkkejä otetaan Linux-järjestelmistä.

  • Käyttäjien tilit ja sisäänkirjautuminen
    • salasanajärjestelmä, salasanan kryptattu tallennus ja talletuspaikan kätkeminen
    • kotihakemisto, käyttäjän profiili; järjestelmän tasolla /etc/profile ja /etc/passwd; käyttäjällä .login ja .cshrc (tai .profile).
    • pääkäyttäjän (rootin, superuserin) erioikeudet, muita erityiskäyttäjiä (mm. tietyt palveluprosessit kuten tulostin- tai www-palvelin); tilannekohtaisten root-oikeuksien antaminen muille kuin ylläpitäjälle (sudo-komento, "s. user and do", missä s:n tulkinta on kehittynyt: super -> switch -> substitute).
    • ryhmät (oletusryhmä, muita ryhmiä; joissain toteutuksissa kuulutaan kullakin hetkellä vain yhteen ja erikseen pitää siirtyä toiseen).
  • Pääsynvalvonta etenkin tiedostoihin:
    • tavallinen eli harkinnanvarainen, perusmallina tiivistetty pääsylista: "ugo"-toimijat (user, group, others) ja "rwx"-toiminnat (x=execute eli suoritus). Myös tarkemmat ACL:t voivat olla käytössä (esim. AIX, HP-UX, Linux). On tiedettävä, miten ACL:t jäsennetään, etenkin jos on ristiriitaisuuksia tai jokerimerkkejä.
    • Muita seikkoja: käyttöoikeuksien tai omistajuuden muuttaminen, oletussuojaukset (umask, jonka bitit poistavat oikeusbittejä rwx'rwx'rwx:stä), niiden vaikutus tiedostoja luotaessa, kopioitaessa ja siirrettäessä; tiedoston tuhoaminen: onko lopullista, välivaihe vai pelkästään nimen ja tunnustietueen linkityksen poisto (kuten Linuxissa).
    • eritasoisten oikeuksien käyttö suid- ja sgid-mekanismeilla ("set {user|group} id", tiedoston käynnistämä prosessi ajetaan tiedoston omistavan käyttäjän tai ryhmän id:llä). Ryhmätekniikka ja sgid voi olla turvallisempi kuin suora suid vaikkapa laitteiden ohjauksessa.
    • eri laitteilla olevien tiedostojen tuominen paikalliseen näkymään mount-komennolla (myös USB-väylältä tai salatulta levyosiolta). Siihen liittyy riskejä, joiden torjumiseksi komennolla on optioina sellaisia kuin 'nosuid' ja 'nodev', joka estää laitteiden näkymisen. Tämä on tarpeen esim. estämään tallenteella olevan kmem-tiedoston kuvautuminen tiedostoksi /dev/kmem, joka edustaa KJ:n ytimen käytössä olevaa muistialuetta.
    • globaali tiedosto /etc/hosts.equiv kertoo, mistä muusta verkon koneesta tulijaa kohdellaan kuin hän olisi paikallisesti autentikoitu samalla tunnuksella, ja käyttäjäkohtainen tiedosto .rhosts voi tarjota vastaavaa toisennimiselle. Näissä on turvaongelmana osoitteen väärennys. Sen vuoksi on syytä käyttää ssh:ta, jonka konfiguraatiossa sanotaan vähintään RhostsAuthentication no. Jos on RhostsRSAAuthentication yes, niin .rhosts-tietoon yhdistetään etäkoneen RSA-autentikointi.
  • Kirjanpito, joka resurssien käytön hallinnan lisäksi liittyy tietoturvaloukkausten havainnointiin (ks. aiempi luku)
    • lastlog, joka liittyy suoraan sisäänkirjautumistilanteeseen; muut lokitiedostot
    • myös tiedostojärjestelmässä on kirjanpitoa: tiedostoa koskevissa tiedoissa (tunnustietue, inode) on kolme aikaa: milloin viimeksi käytetty (atime), muutettu (mtime) ja milloin tunnustietuetta on muutettu (ctime; esim. omistajaa tai oikeuksia).
    • pääkäyttäjä pystyy Unixissa viime kädessä kaikkeen, joten täysin peukaloimattoman tapahtumakirjan pitäminen ei onnistu. Ylläpidon varalle tarvitaan erityisjärjestelyä — hallintoa, verkon yli siirrettävää lokia, "write-once"-välineitä.
  • Konfiguraatio ja hallinta
    • asentaminen, konfigurointi; oletusasetusten rajoittaminen; teknisen, käyttäjä- ja turvahallinnon ylläpitäjien oikeuksien mahdollinen eriyttäminen.
    • käyttäjähallinto: käyttäjätietojen päivitys (uudet, muuttuneet, poistuneet); sovellusohjelmien päivitys ja ongelmien ratkonta.
    • turvaylläpito: turvattomien salasanojen etsintä; muiden turva-aukkojen etsintä; hyökkäysten havainnointi ja käsittely.
    • tekninen ylläpito: laitteiden ja käyttöjärjestelmän päivittäminen: turva- ym. paikkaukset, uudet versiot.
Tässä on tiivistettynä tekstin alussa oleva luettelo käyttöjärjestelmien perustavanlaatuisista turvamenettelyistä. Poimi niistä kolme kaikkein perustavinta eristysmenettelyä.
Tilannekohtaisten root-oikeuksien antaminen muille kuin ylläpitäjälle tapahtuu Linux-järjestelmässä
Linux-järjestelmissä tiedostojen ACL- eli pääsynvalvontalista

Todentaminen ja tunnistaminen

Koska todennus eli autentikointi on myöhemmän luvun aiheena, todetaan tässä vain, että perinteisten salasanojen rinnalle tai tilalle on tullut monia muita tapoja: älykortteja ja muita tunniste-esineitä sekä sormenjälkiä ja muita biometriikkoja. Olipa todennustapa mikä tahansa, käyttöjärjestelmä ylläpitää jokaiselle käyttäjätunnusta ja mahdollisesti tietoa erilaisten ryhmien jäsenyydestä, joka vaikuttaa pääsyoikeuksiin (esim. opiskelija, opettaja, ylläpitäjä). Useimmat käyttöjärjestelmät liittävät käyttäjän identiteetin jokaiseen hänen prosessiinsa ja seuraavat sen kautta tiedostojen omistajuutta ja käyttöoikeuksia.

Valtuustiedoiksi kutsutaan niitä tietoja, joita käyttäjä toimittaa todennustaan varten, ja samaa termiä käytetään niistä tiedoista, joita vastaan käyttöjärjestelmä tekee vertailun. Edellisiä ei tarvita käytön jälkeen, mutta jälkimmäisten säilyttäminen turvallisella tavalla on ratkaisevan tärkeää. Useat nykyaikaiset käyttöjärjestelmät turvautuvat laitteistoon tällaisten arkaluonteisten tietojen suojaamiseksi. Ne voivat esimerkiksi käyttää TPM-moduulia tai käyttää erillistä virtuaalikonetta valtuustietosäilönä, jotta edes rämettynyt virtuaalikone ei pääse suoraan valtuustietoihin. Kryptoavainten tallennuksessa on samoja piirteitä, ja osa valtuustiedoista onkin nimenomaan avaimia.

Pääsylistat

Multicsin tiedostojärjestelmässä jokaiselle järjestelmän tietoyksikölle oli pääsyluettelo eli pääsynvalvontalista (access control list, ACL). Käsitteellisesti ACL on käyttäjiä ja tietoyksiköitä sisältävä taulukko, joka määrittää kullekin tietoyksikölle, millä käyttäjillä on siihen minkälaiset käyttöoikeudet. Useimmat nykyaikaiset käyttöjärjestelmät ovat ottaneet käyttöön jonkin muunnelman ACL-listoista, tyypillisesti tiedostojärjestelmälle. Unix-pohjaisissa järjestelmissä oletusarvoinen pääsynvalvonta on hyvin yksinkertainen. Jokaisen tiedoston omistaa yksi käyttäjä ja yksi ryhmä. Jokainen käyttäjä voi kuulua yhteen tai useampaan ryhmään. Johdannossa jo mainitut bitit rwx ilmaisevat lukemisen, kirjoittamisen ja suorittamisen oikeudet (hakemistojen tapauksessa x on pääsyä sen tiedostoihin, kunhan niillä on r, w tai x). Bittejä on kolme ryhmää, joista ensimmäiset koskevat omistajaa, toinen ryhmän jäseniä ja kolmas kaikki muita saman koneen käyttäjiä.

Unix-tiedostojen perusoikeudet ovat siis yksinkertaiset, mutta nykyaikaiset järjestelmät (kuten Linux ja Windows) mahdollistavat myös laajemmat ACL-luettelot (esim. nimenomaiset käyttöoikeudet useille käyttäjille tai ryhmille). Aina kun joku yrittää lukea, kirjoittaa tai käyttää tiedostoa, käyttöjärjestelmä tarkistaa, ovatko tarvittavat käyttöoikeudet ACL:ssä. Lisäksi Unixin pääsynvalvontakäytäntö on tyypillisesti harkinnanvarainen, koska tiedoston omistaja voi asettaa nämä oikeudet muille.

Harkinnanvaraisen eli DAC:n lisäksi Multics otti käyttöön MAC:n. Vaikka samaan pääseminen kesti muilta kauan, MAC on nykyään tarjolla monissa käyttöjärjestelmissä, jotka ovat saaneet inspiraationsa Multicsista. Linux tarjoaa jopa puitteet, jotka mahdollistavat erilaisten AC-ratkaisujen kytkemisen viitemonitoreihin. Ne siis monitoroivat eli vahtivat AC-asetustan mukaisesti viittauksia, mikä tarkoittaa tietoturvan kannalta araksi määriteltyjen toimintojen tarkistamista. Useita MAC-ratkaisuja on olemassa. Tunnetuin lienee Security-Enhanced Linux (SELinux). Se on joukko tietoturvaa parantavia Linux-paikkauksia. Alun perin sen kehitti Yhdysvaltain kansallinen turvallisuusvirasto (NSA).

SELinux antaa käyttäjille ja prosesseille kontekstin, joka koostuu kolmesta merkkijonosta: käyttäjänimi, rooli ja toimialue. Roolin mainitseminen antaa oikean viitteen siitä, että myös RBAC on mahdollinen. Kuitenkin monet järjestelmät ovat ottaneet MAC-käyttöön vain toimialueen ja asettavat käyttäjänimen ja roolin samaksi kaikille käyttäjille. Prosessien lisäksi SELinux-kontekstit liittyvät resursseihin, kuten tiedostoihin, verkkoportteihin ja laitteisiin. Tällaisen kokoonpanon perusteella järjestelmänvalvojat voivat määrittää järjestelmän laajuiset käytännöt pääsynvalvontaa varten. He voivat esimerkiksi yksinkertaisesti säätää, minkä toimialueen prosessit saavat suorittaa resurssille tiettyjä toimintoja (r, w, x, tai yhteyden muodostus). Asetettava politiikka voi myös olla paljon monimutkaisempi, sisältäen esim. useita suojaustasoja ja tietovuon valvonnan Bell-LaPadula -mallin mukaisesti.

Pakotettu pääsynvalvonta SELinuxin kaltaisissa järjestelmissä perustuu yhteen järjestelmän laajuisen käytäntöön. Pääkäyttäjä asettaa sen eikä se muutu. Epäluotetut prosessit eivät voi määritellä eikä päivittää omia käytäntöjään. Sellaista on kyllä kokeiltu tutkimusjärjestelmissä.

Teoreettinen perusmalli pääsynvalvonnalla on, että kaikkien toimijoiden oikeudet kaikkiin kohteisiin luetellaan matriisissa, jossa on rivi kutakin toimijaa ja sarake kutakin kohdetta varten. Tämä on teoriassa täydellinen menetelmä, mutta ei kovin käytännöllinen, jos olioita erotellaan vähänkään hienojakoisesti. Suurin osa valtavasta matriisista jäisi tyhjäksi ja toisaalta monet rivit ja sarakkeet olisivat suurelta osalta toistensa kopioita. Matriisista on helppo oivaltaa, että
Pääsynvalvonnan harkinnanvaraisuus SELinuxissa tarkoittaa, että

Kyvykkyydet (syventävä)

Vaihtoehto ACL-tekniikalle ovat kyvykkyydet, capabilities, peräisin jo vuodelta 1966. Toisin kuin ACL:t, kyvykkyydet eivät vaadi resurssikohtaista hallintaa, jossa olisi tarkat tiedot siitä, kuka saa suorittaa minkä toiminnon. Sen sijaan käyttäjät esittävät kyvykkyyden, joka itsessään todistaa, että pyydetty pääsy on sallittu. Kyvykkyyden hallussapito antaa kaikki siinä määritellyt oikeudet ja aina kun prosessi haluaa suorittaa toiminnon resurssille, sen tulee esittää asianmukainen kyky.

Tietenkin kykyjen väärentämisen pitäisi olla mahdotonta, jotta käyttäjät eivät antaisi itselleen mielivaltaista pääsyä mihin tahansa haluamaansa kohteeseen. Näin ollen käyttöjärjestelmän tulee joko tallentaa kyvykkyys turvalliseen paikkaan (esimerkiksi ytimeen) tai suojata se väärentämiseltä käyttämällä erityistä laitteistoa tai ohjelmistopohjaista salausta. Laitteiston tapauksessa käyttöjärjestelmä voi tallentaa prosessin kyvykkyydet suojatussa muistissa olevaan taulukkoon, ja aina kun prosessi haluaa suorittaa toiminnon tiedostolle, se antaa vain viittauksen kykyyn (esim. tiedostokuvaajan) mutta ei itse käsittele sitä. Ohjelmistotapauksessa prosessi voi itse käsitellä kyvykkyyttä, mutta kaikki yritykset muokata sitä sopimattomalla tavalla havaitaan kryptografisen tarkistuksen avulla.

Kyvykkyydet ovat erittäin joustavia ja mahdollistavat kätevät delegointikäytännöt. Esimerkiksi, jos jonkin kyvykkyyden kokonaan omistava prosessi voi siirtää sen toiselle prosessille. Tällöin prosessi voi delegoida joko samat käyttöoikeudet tai vaihtoehtoisesti näiden oikeuksien osajoukon. Näin ollen harkinnanvarainen pääsynvalvonta on helppoa. Toisaalta joissakin tilanteissa ei ehkä ole toivottavaa kopioida ja levittää kyvykkyyksiä mielivaltaisesti. Tästä syystä useimmat kyvykkyyspohjaiset järjestelmät lisäävät ominaisuuksiin muutaman bitin osoittamaan tällaisia rajoituksia: onko kopiointi sallittua, pitäisikö kyvykkyyden käyttöikä rajoittaa proseduurin kutsuvaiheeseen jne.

Verrattaessa ACL-luetteloita ja kyvykkyyksiä havaitaan lisäksi, että ACL-luettelot perustuvat tyypillisesti käyttäjiin (“käyttäjä, jolla on id sejase, saa lukea ja kirjoittaa tätä kohdetta”), kun taas kyvykkyydet voivat olla erittäin hienojakoisia. Voidaan esimerkiksi käyttää erilaisia kyvykkyyksiä tietojen lähettämiseen ja vastaanottamiseen. Vähimpien oikeuksien periaatteen mukaisesti jokaisen prosessin ajaminen käyttäjän “täydellä teholla” ei ole yhtä turvallista kuin prosessin suorittaminen vain sen hankkimien kyvykkyyksien perusteella. Ohjelman käynnistäneen käyttäjän valtuuksilla ajaminen, kuten nykyaikaisissa käyttöjärjestelmissä usein tapahtuu, tunnetaan termillä ambient authority. Se rikkoo paljon todennäköisemmin vähimpien oikeuksien periaatetta kuin prosessin varustaminen hienojakoisilla kyvykkyyksillä sen mukaan mitä se tarvitsee. Lisäksi kyvykkyydet eivät salli prosessin edes nimetä kohdetta ilman määriteltyä kykyä, kun taas ACL-tekniikka sallii kaikkien kohteiden nimeämisen, koska pääsyn tarkistus tapahtuu vasta, kun prosessi yrittää toimintoa. Vielä ACL-tekniikan varjopuolena voi olla se, että ACL:t tulevat erittäin suuriksi käyttäjien, käyttöoikeuksien ja kohteiden määrän kasvaessa.

Toisaalta joidenkin käyttäjien käyttöoikeuden peruuttaminen tiettyihin kohteisiin ACL-tekniikassa on helppoa: poistetaan vain käyttöoikeudet kyseisten kohteiden ACL:stä. Kyvykkyyksien tapauksessa tiettyyn kohteeseen voimassaolevien oikeuksien poistaminen on vaivalloista, kun ei ehkä edes tiedetä, millä käyttäjillä ja prosesseilla on kyvykkyys. Epäsuoran viittaustason lisääminen voi auttaa jonkin verran. Voidaan esimerkiksi asettaa kyvykkyydet osoittamaan epäsuoraa kohdetta, joka puolestaan osoittaa todelliseen kohteeseen. Tämän kyvykkyyden mitätöimiseksi (kaikilta käyttäjiltä/prosesseilta) käyttöjärjestelmä voi mitätöidä kyseisen epäsuoran objektin. Mutta mitä voidaan tehdä, jos halutaan peruuttaa kyvykkyys vain osalta prosesseja? Vaikka ratkaisuja on olemassa, muitakin siis kuin kaikkien läpikäynti, kyvykkyyksien peruuttaminen on edelleen niiden hankalin ominaisuus.

Nykyään monilla suurilla käyttöjärjestelmillä on myös ainakin jonkin verran tukea kyvykkyyksille. Esimerkiksi L4-mikroydin, jollainen on monissa mobiililaitteissa, on sisältänyt kyvykkyydet vuoden 2008 versiosta alkaen. Formaalisti verifioitu “turvaversio” seL4 perustuu samanlaiseen pääsynvalvontaan. Linuxissa on ollut vuodesta 1997 ns. POSIX capabilities, mutta niissä on kyse rajallisesta ja melko erilaisesta asiasta kuin varsinaisissa kyvykkyyksissä.

Muistin suojaus ja osoiteavaruudet (syventävä)

Prosessin ei normaalisti pitäisi pystyä lukemaan toisen prosessin tietoja ilman asianmukaista pääsynvalvontatarkistusta. Multics ja lähes kaikki sitä seuranneet käyttöjärjestelmät (kuten Unix ja Windows) eristävät tiedot antamalla kullekin prosessille (a) oman prosessorin tilan (rekisterit, ohjelmalaskuri jne.) ja (b) oman osuuden muistista. Aina kun käyttöjärjestelmä päättää vaihtaa käynnissä olevan prosessin P1 tilalle prosessin P2, se tekee ns. kontekstinvaihdon: Se pysäyttää ensin P1:n ja tallentaa suorittimen koko tilan muistiin alueelle, johon muut prosessit eivät pääse käsiksi. Seuraavaksi se lataa P2:n vastaavan tilan muistista CPU:hun (tai alustaa sen), säätää kirjanpitoa, joka määrittää, mitkä fyysisen muistin osat ovat käytettävissä, ja alkaa suorittaa P2:ta sen ohjelmalaskurin osoittamassa osoitteessa. Koska käyttäjäprosessit eivät voi manipuloida kirjanpitoa, P2 ei voi itse päästä käsiksi mihinkään P1:n tietoihin. Useimmat nykyaikaiset käyttöjärjestelmät pitävät muistikirjanpitoa sivutaulujen avulla ja tallentavat rekisteriin osoittimen ylimmän tason tauluun. Rekisteri on osa prosessorin tilaa, joka tallennetaan ja palautetaan kontekstinvaihdossa.

Sivutaulurakenteen pääasiallinen käyttötarkoitus on antaa jokaiselle prosessille oma virtuaalinen osoiteavaruutensa, vaikka fyysisen muistin määrä voi olla paljon pienempi. Turvallisuuden kannalta rakenteen keskeinen seuraus on, että prosessi voi käyttää muistissa olevia tietoja vain, jos sen sivutauluissa on kuvaus (mapping) sille. Kuvausta hallitsee käyttöjärjestelmä, joka voi näin ollen päättää tarkalleen, mikä muisti on yksityinen ja mikä muisti jaetaan ja kenen kanssa. Suojauksen toteuttaa laite, muistinhallintayksikkö, MMU (Memory management unit). Jos virtuaalisen osoitteen kuvausta fyysiseksi ei löydy pienestä mutta erittäin nopeasta välimuistista, TLB:stä (Translation lookaside buffer), MMU etsii sen kulkemalla vaiheittain sivutaulusta toiseen.

Sivutaulut ovat tärkein tapa nykyaikaisissa käyttöjärjestelmissä hallita muistin käyttöä. Toinen tapa, joka on jäänyt syrjään, on segmentointi. Perusidea (ks. johdanto) on hyvä ymmärtää, koska samantapaista monimutkaista ja monitasoista osoitekäännöstä esiintyy virtualisoiduissa ympäristöissä.

Laitteistopohjainen muistin turvaaminen (syventävä)

Muistin suojaaminen laitteiston tuella on edelleen kehittyvä ala, joskin vanhoja asioita keksitään uudelleen. Intelin prosessoreiden MPK-muistisuojausavaimet (Memory protection keys, 2010-luvulla) ovat siitä esimerkki (vrt. keskustelu 2015, jossa viitataan 1960-luvulle. Kai pyöräkin kannattaa keksiä uudelleen, kun ajoneuvo, eli tässä muisti, antaa uusia mahdollisuuksia). MPK sallii sivutaulun alkiossa aiemmin käyttämättömänä olleen neljän bitin asettamisen johonkin 16 arvosta. Näiden avulla kehittäjät voivat osioida käyttämänsä muistin suojausalueisiin ja sallia esimerkiksi vain tietyn kryptokirjaston päästä käsiksi salausavaimiin. Lisänä on myös uusi 32-bittinen rekisteri, jossa kuhunkin avaimen arvoon voi liittää 2 bittiä osoittamaan, sallitaanko tällä avaimella merkittyjen sivujen lukeminen tai kirjoittaminen. Rekisterin kautta pääsyä voi helposti säätää joutumatta käymään kaikissa käsiteltävän suojausalueen sivujen viittaustaulualkioissa.

Jotkin prosessorimallit tukevat vielä hienojakoisempaa muistisuojausta. ARM-prosessorissa sen nimenä on MTE, memory tagging extensions. Idea on yksinkertainen mutta voimakas (eikä sekään kovin uusi). Prosessori asettaa jokaiselle muistialkiolle (esim. 16 tavun kokoiselle) niin sanotun tagin laitteistoon. Vastaavat tunnisteet saa myös jokainen muistiosoitin (joka on ohjelmallisesti tallentuva tieto). Tunnisteet ovat melko pieniä, esim. 4 bittiä, joten ne voidaan kätevästi tallentaa 64-bittisen osoittimen ylimpään tavuun, joka muuten jäisi käyttämättä. Aina kun ohjelma varaa muistia, muistiallokaattori määrittää sille satunnaisen tagin, ja asettaa sen muistiosoittimeen. Tästä lähtien viittaaminen osoittimella muistiin on sallittua vain, jos tagit vastaavat toisiaan. Tämä estää tehokkaasti useimmat tilalliset ja ajalliset muistivirheet.

Samaan aikaan joissakin prosessoreissa, varsinkin pienitehoisissa laitteissa, ei ole edes täysimittaista MMU:ta. Sen sijaan niissä on paljon yksinkertaisempi Memory Protection Unit (MPU). Se palvelee vain muistin suojaamista ja tekee sen tavalla, joka muistuttaa edellä mainittua MPK-tekniikkaa. MPU-mallissa käyttöjärjestelmät määrittelevät joukon muistialueita, joilla on tietyt käyttöoikeudet ja attribuutit. Esimerkiksi ARMv8-M-prosessorien MPU tukee jopa 16 aluetta. Määritysten mukaisesti MPU sitten valvoo kaikkea prosessorin muistin käyttöä, sekä käskyjen että datan osalta.

Edellä oletettiin, että käyttöjärjestelmä tarvitsee suojaa epäluotettavia käyttäjäsovelluksia vastaan. Voi olla myös toisin päin. Ehkä käyttäjällä on jokin tietoturvaan liittyvää sovellus mahdollisesti vaarantuneessa käyttöjärjestelmässä tai pilvessä, jossa hän ei välttämättä halua luottaa palveluntarjoajaan. Pitäisi voida suojata tiedot ja sovellukset luottamatta muihin ohjelmistoihin. Tätä tarkoitusta varten prosessorit voivat tarjota laitteistotukea erittäin herkän koodin suorittamiseen suojatussa, eristetyssä ympäristössä. Tällaisia luotettuja suoritusympäristöjä ovat ARM:n TrustZone ja Intelin Software Guard Extension (SGX). Ne tarjoavat hieman erilaisia primitiivejä. Esimerkiksi SGX-ympäristössä toimiva koodi on tarkoitettu osaksi normaalia käyttäjäprosessia. Sen käyttämä muisti salataan aina heti, kun se lähtee prosessorista. Lisäksi SGX tarjoaa laitteistotuen todistukseen, jolla toinen osapuoli - mahdollisesti etänä - voi vakuuttua, että koodi toimii eristettynä ja että se on oikea. ARM TrustZone puolestaan eristää tavallisen käyttöjärjestelmän ja käyttäjäsovellukset “suojatusta maailmasta”, jossa on oma pienempi käyttöjärjestelmä ja pieni määrä tietoturvaan liittyviä sovelluksia. Suojatun maailman koodia kutsutaan tavalla, joka muistuttaa tavanomaista sovellusten systeemikutsua. Yksi mielenkiintoinen erikoisympäristöjen – sellaisten kuin TrustZone tai Intelin SMM-tila – sovellus on käyttää niitä tavallisen käyttöjärjestelmän eheyden ajonaikaiseen valvontaan – yrityksenä havaita ajoissa haittaohjelmat tai rootkitit. Vaikka luotetut ympäristöt liittyvät läheisesti käyttöjärjestelmien turvallisuuteen, niihin ei mennä tässä pidemmälle. On kuitenkin hyvä tiedostaa, että viime vuosina laitteistopohjaisista luotetuista ympäristöistä löydetty useita sivukanavia.

Voi olla niin, että käyttöjärjestelmä on kunnossa, mutta laitteisto ei. Haitalliset tai vialliset laitteistot voivat käyttää suoraa muistiviittausta (DMA) lukeakseen tai korvatakseen dataa, jonka ei pitäisi olla niiden ulottuvilla. Lisäksi joidenkin standardien (kuten Thunderbolt over USB-C) avulla tietokoneen PCIe-linkit voivat olla suoraan alttiina laitteille, jotka käyttäjä kytkee tietokoneeseen (PCIe eli Peripheral Component Interconnect Express, käyttää pisteiden välistä linkkejä toisin kuin PCI, joka on väylä).

Valitettavasti käyttäjän on vaikea olla varma, ettei näyttö- tai virtakaapeli sisällä haitallisia piirejä. Osittainen parannuskeino useimmissa arkkitehtuureissa on nykyään erityinen MMU laitteilta ja laitteille siirrettävään dataan: IOMMU, Input-Output Memory Management Unit. Se palvelee DMA-laitteiden virtuaalisten osoitteiden yhdistämistä fyysisiin osoitteisiin jäljittelemällä muistin sivupohjaista suojausta. Toisin sanoen laitteet voivat viitata virtuaalimuistin osoitteeseen, jonka IOMMU muuntaa todelliseksi osoitteeksi, tarkistaa oikeudet ja pysähtyy, jos sivua ei ole kuvattu (mapped) laitteelle tai suojabitit eivät vastaa pyydettyä pääsyä. Vaikka tämä tarjoaa jonkin verran suojaa haitallisia laitteita (tai jopa ohjaimia) vastaan, on tärkeää ymmärtää, että IOMMU on suunniteltu helpottamaan virtualisointia, eikä sitä pitäisi nähdä oikeana tietoturvaratkaisuna. On monia asioita, jotka voivat mennä vikaan. Esimerkiksi ylläpitäjä haluaa ehkä peruuttaa laitteen käyttöoikeudet muistisivulle. Koska IOMMU-sivutaulujen päivittäminen on hidasta, ei ole harvinaista, että käyttöjärjestelmät viivästävät tätä toimintoa ja toteuttavat sen vasta muiden toimintojen lomassa. Seurauksena on, että saattaa kulua pieni aika, jonka kuluessa laitteella on edelleen pääsy muistisivulle, vaikka näyttää siltä, että nämä oikeudet on jo peruutettu.

Kasvava transistoreiden määrä pinta-alaa kohti antaa valmistajille tilaisuuden sijoittaa prosessorisiruilleen yhä enemmän laitteistolaajennuksia. Yllä mainitut eivät ole viimeisiä turvallisuuteen liittyviä laajennuksia. Muita esimerkkejä ovat salausyksiköt, muistin salaus, käskyt laajennettujen sivutaulujen tehokkaaseen vaihtamiseen ja osoittimen todennus (jossa laitteisto havaitsee osoitinarvojen muuttamisen). Uusia ominaisuuksia ilmaantuu tulevaisuuden prosessoreihin ja käyttöjärjestelmien on mukauduttava voidakseen käyttää niitä mielekkäällä tavalla. Laajempi näkemys näistä ongelmista löytyy laitteistoa käsittelevästä pääluvusta.

Suojakehät (syventävä)

Yksi Multicsin vallankumouksellisista ideoista olivat suojakehät (protection rings). Ne muodostavat hierarkian, jossa sisäkehällä (0) on suurimmat oikeudet ja uloimmalla pienimmät. Epäluotetut käyttäjäprosessit suoritetaan ulkokehällä, kun taas luotettu ja etuoikeutettu ydin, joka on suoraan vuorovaikutuksessa laitteiston kanssa, suoritetaan kehällä 0, ja muita kehiä voidaan käyttää enemmän tai vähemmän etuoikeutetuissa järjestelmäprosesseissa. (Englanniksi sanotaan muuten “in the ring” mutta suomeksi yleensä “kehällä”.)

Suojakehät edellyttävät yleensä laitteistotukea. Useimmat yleiskäyttöiset prosessorit tarjoavat sitä nykyään, vaikka kehien määrä voi vaihdella. Esimerkiksi Honeywell 6180 tuki kahdeksaa kehää (1970-luvulla), Intelin x86 tukee neljää, ARM v7 kolmea (ynnä yhtä ylimääräistä TrustZonelle) ja PowerPC tuki kahta (1990-luvulla). Kuten jäljempänä nähdään, tarina mutkistuu, koska jotkin nykyaikaiset prosessorit ovat myös ottaneet käyttöön enemmän ja erilaisia prosessoritiloja. Toistaiseksi todetaan yksinkertaistaen, että useimmat tavalliset käyttöjärjestelmät käyttävät vain kahta kehää: yksi käyttöjärjestelmälle ja toinen käyttäjäprosesseille.

Aina kun koodi tarvitsee toimintoa, joka vaatii enemmän oikeuksia kuin sillä on, se “kutsuu” alempaa kehää pyytääkseen tämän toiminnon suorittamista palveluna. Siten vain luotettu, etuoikeutettu koodi voi suorittaa kaikkein arkaluontoisimpia komentoja tai käsitellä kaikkein arkaluontoisimpia tietoja. Ellei prosessi huijaa etuoikeutetumpaa koodia tekemään jotain, mitä sen ei pitäisi tehdä, kehät tarjoavat tehokkaan suojan. Multicsin alkuperäinen idea oli, että siirtyminen kutsujen välillä tapahtuisi erityisten kutsuporttien kautta, jotka pakottavat tarkan valvonnan ja sovittelun. Ulomman kehän koodi ei voi kutsua mitä tahansa toimintoa sisäkehällä, vaan vain ennalta määritettyä liittymää, jossa kutsu ja sen argumentit tarkistetaan suhteessa politiikkaan.

Vaikka prosessorit, kuten x86, tukevat edelleen kutsuliittymiä (call gates), harvat käyttöjärjestelmät käyttävät niitä, koska ne ovat suhteellisen hitaita. Sen sijaan käyttäjäprosessit siirtyvät käyttöjärjestelmän ytimeen (tekevät systeemikutsun, system call) suorittamalla ohjelmistokeskeytyksen (“trap”), jonka käyttöjärjestelmä käsittelee. Yleisesti tämä tapahtuu erityisellä, erittäin tehokkaalla järjestelmäkutsulla, jolla on arkkitehtuurista riippuen nimenä SYSCALL, SYSENTER, SVC, SCALL, jne. Monet käyttöjärjestelmät sijoittavat järjestelmäkutsun argumentit ennalta määrättyyn joukkoon rekistereitä. Kuten kutsuliittymät, myös trapit ja järjestelmäkutsu varmistavat, että suoritus jatkuu käyttöjärjestelmän ennalta määrätyssä osoitteessa, jossa oleva koodi tarkastaa argumentit ja kutsuu sitten sopivaa toimintoa.

Käyttöjärjestelmää kutsuvan käyttäjäprosessin lisäksi useimmat käyttöjärjestelmät sallivat myös ytimen kutsua käyttäjäprosessia. Esimerkiksi Unix-pohjaiset järjestelmät tukevat signaaleja, joiden avulla käyttöjärjestelmä lähettää ilmoituksia käyttäjäohjelmalle: virhe, vanhentunut ajastin, keskeytys, viesti toisesta prosessista jne. Jos käyttäjäprosessi on rekisteröinyt signaalille käsittelyrutiinin, käyttöjärjestelmä pysäyttää prosessin nykyisen suorituksen, tallentaa sen tilan pinoon niin sanottuun signaalikehykseen ja jatkaa suoritusta signaalinkäsittelijällä. Kun signaalinkäsittelijä palaa, prosessi suorittaa systeemikutsun sigreturn, joka saa käyttöjärjestelmän palauttamaan pinossa olevan prosessorin tilan ja jatkamaan prosessin suorittamista.

Suojausalueiden, kuten käyttöjärjestelmän ytimen ja käyttäjätilaprosessien, välinen raja on hyvä paikka tarkistaa sekä systeemikutsut että niiden argumentit. Esimerkiksi kyvykkyyspohjaisissa käyttöjärjestelmissä ydin tarkistaa kyvykkyydet, ja Minix-3:n kaltaisissa käyttöjärjestelmissä prosessit saavat tehdä vain tietynlaisia kutsuja, joten kaikki yritykset tehdä jotain ennalta hyväksytyn luettelon ulkopuolelta merkitään rikkomukseksi. Samoin Windows- ja Unix-pohjaisten käyttöjärjestelmien on tarkistettava monien systeemikutsujen argumentit. Esimerkiksi tärkeä systeemikutsu on read, jolla käyttäjän prosessi pyytää tietojen kopiointia tiedostosta tai soketista puskuriin. Käyttöjärjestelmä tarkistaa, omistaako prosessi muistialueen, josta tieto pitäisi lukea. Vastaava koskee käänteistä systeemikutsua write, jossa tarkistus tehdään kirjoituskohteen omistuksen suhteen.

Systeemikutsun suorittamisen jälkeen käyttöjärjestelmä antaa kontrollin kutsuneelle prosessille. Myös tässä käyttöjärjestelmän on huolehdittava, ettei se tuota järjestelmän turvallisuutta vaarantavia tuloksia. Jos prosessi esimerkiksi käyttää systeemikutsua mmap pyytääkseen käyttöjärjestelmää lisäämään muistia osoiteavaruuteensa, käyttöjärjestelmän tulee varmistaa, että sen luovuttamat muistisivut eivät enää sisällä arkaluonteisia tietoja aiemmasta prosessista - esim. alustamalla jokainen tavu nollaksi.

Alustusongelmat voivat olla hyvin hienovaraisia. Esimerkiksi kääntäjät ottavat usein käyttöön täytetavuja tietorakenteissa tasausta varten. Koska nämä täytetavut eivät näy ohjelmointikielen tasolla ollenkaan, kääntäjä ei välttämättä itse alusta niitä nollaksi. Suojausrikkomus kuitenkin tapahtuu, jos käyttöjärjestelmä palauttaa tällaisen tietorakenteen vastauksena systeemikutsuun ja alustamattoman täytteen kohdalla on luottamuksellisia tietoja ytimestä tai muusta prosessista.

Jopa aiemmin mainittu signalointi Unix-järjestelmissä on mielenkiintoinen turvallisuuden kannalta. Siinähän sigreturn ottaa pinossa olevan prosessorin tilan ja palauttaa sen. Hyökkääjä saattaa voida turmella prosessin pinon ja tallentaa siihen väärennetyn signaalikehyksen. Jos hyökkääjä pystyy sitten myös toteuttamaan sigreturn-kutsun, hän tulee asettaneeksi koko prosessorin tilan kaikkine rekisteriarvoineen samalla kertaa. Tämä on muunnelma hyökkäyksestä, jossa aliohjelmasta paluu ohjautuu uudelleen, ks. Return-oriented programming (ROP) ja SigROP (SROP).

Kehän alituksia (syventävä)

Suojauskehien tilanne on nykyään hieman hämmentävä, sillä viimeaikaiset prosessorit tarjoavat hypervisorin käyttöön virtualisointikäskyjä, joiden avulla on pääsy laitteistoihin kehällä 0. Tätä varten prosessorit ovat lisänneet jotain mikä näyttää kehä 0:n alapuoliselta kehältä. Koska x86-prosessoreissa kehästä 0 on tullut synonyymi käyttöjärjestelmän ytimelle (ja kehästä 1 käyttäjäprosesseille), tästä uudesta hypervisor-kehästä käytetään yleisesti nimitystä kehä –1. Se osoittaa myös, että omien virtuaalikoneidensa käyttöjärjestelmät voivat suorittaa kehä 0:n käskyjä suoraan. Tarkkaan ottaen kehän –1 tarkoitus on toisenlainen kuin alkuperäisillä kehillä, ja nimitys voi siis johtaa harhaan.

Asiat voivat mennä vieläkin monimutkaisemmaksi, koska joissakin nykyaikaisissa prosessoreissa on vielä muitakin tiloja. Esimerkiksi x86 tarjoaa niin sanotun SMM-tilan (System Management Mode). Kun järjestelmä käynnistyy, laiteohjelmisto hallitsee laitteistoa ja valmistelee järjestelmän käyttöjärjestelmää varten. Kuitenkin, kun SMM on käytössä, laiteohjelmisto saa taas hallinnan, kun tietty keskeytys lähetetään CPU:lle. Laiteohjelmisto voi esimerkiksi ilmoittaa, että se haluaa saada keskeytyksen aina, kun virtapainiketta painetaan. Siinä tapauksessa tavallinen suoritus pysähtyy ja laiteohjelmisto ottaa vallan. Se voi esimerkiksi tallentaa prosessorin tilan, tehdä tarpeelliset toimensa ja sitten jatkaa käyttöjärjestelmää sen säännöllistä sammutusta varten. Tavallaan SMM nähdään toisinaan muita kehiä alempana tasona (kehä –2). Lopuksi Intel jopa lisäsi kehän –3 Intel Management Enginen (ME) muodossa. ME on täysin itsenäinen järjestelmä, joka on nyt lähes kaikissa Intelin piirisarjoissa; se käyttää salaista ja täysin itsenäistä laiteohjelmistoa erillisessä mikroprosessorissa ja on aina aktiivinen: käynnistyksen aikana, kun kone on käynnissä, kun se on lepotilassa ja jopa silloin, kun se on sammutettu. Niin kauan kuin tietokone on kytkettynä virtaan, on mahdollista kommunikoida ME:n kanssa verkon kautta ja esimerkiksi asentaa päivityksiä. Vaikka se on erittäin tehokas, sen toiminnallisuus on suurelta osin tuntematon, paitsi että se käyttää omaa pientä käyttöjärjestelmää (versio 11 pohjautuu tiettävästi Minix-3:een). Pääsuorittimen mukana tulevat lisäprosessorit – olipa kyseessä sitten ME tai sitä vastaava Applen T2- tai Googlen Titan-siru – nostavat esiin mielenkiintoisen kysymyksen: pystyykö pääsuorittimella toimiva käyttöjärjestelmä edes täyttämään nykypäivän tietoturvavaatimukset? Ainakin trendi näyttää olevan täydentää sitä erityiskäyttöisillä turvajärjestelmillä (laitteistolla ja ohjelmistolla).

Sulautetut järjestelmät ja IoT-laitteet

Monet edellä kuvatuista ominaisuuksista löytyvät tavalla tai toisella useimmista yleiskäyttöisistä prosessoriarkkitehtuureista. Tämä ei kuitenkaan välttämättä pidä paikkaansa IoT:ssä tai sulautetuissa järjestelmissä yleensä, joissa käytetään yleisesti räätälöityjä käyttöjärjestelmiä. Yksinkertaisissa mikro-ohjaimissa ei tyypillisesti ole MMU:ita, ja joskus ei edes MPU:ita, suojakehiä tai muita edistyneitä ominaisuuksia, joita sovelletaan yleisissä käyttöjärjestelmissä. Järjestelmät ovat yleensä pieniä (mikä vähentää hyökkäyspinta-alaa) ja sovellukset ovat luotettavia (ja mahdollisesti verifioituja). Siitä huolimatta laitteiden sulautettu luonne vaikeuttaa niiden turvallisuuden tarkistamista tai jopa testaamista. Kaikkialla, missä näillä laitteilla on rooli turvallisuuteen liittyvissä toimissa, eristyksen ja välityksen avulla tapahtuvaa turvallisuutta tulisi valvoa ulkoisesti, ympäristön toimesta. Laajempia IoT-ongelmia käsitellään aikanaan kyberfyysisten järjestelmien yhteydessä (CyBOK-tekstin luku 21).

Tämän luvun alussa esitettiin alla tiivistetty luettelo tarkasteltavista asioista. Minkä näistä mainittiin tässä viimeisessä alaluvussa yleensä jäävän pois, jos kyseessä ovat sulautetut järjestelmät?
Palautusta lähetetään...