GDPR – Tietosuoja ja sovelluskehitys osa 1

Pyydän jo etukäteen anteeksi, tämä jos mikään on TL;DR settiä. Asiaa on vain aika vaikeaa kuvata ytimekkäästi, tahoja on monia. Jos artikkelin pituus haittaa lukemista, asennoidu lukemaan vaikka yksi kappale päivässä. Ota mukaan yhtälöön kupillinen mielijuomaasi, ja hyvää musiikkia taustalle, niin kyllä se siitä. Lupaan että substanssi on rautaa, kuittaa kommentteihin jos olet eri mieltä – saa toki kuitata vaikka olisi samaakin 😉

Totean myös, että en ole lakimies, ja tällä hetkellä tietosuoja-asetuksessa on monia alueita joista edes lakimiehet eivät voi sanoa mitään varmaa. Paras viisaus on jälkiviisaus, ja vuonna 2027 osaan kertoa faktaa. Tässä kohtaa kaikki blogautusten pointit edustavat omia näkemyksiä, jotka ovat ilman muuta ainutlaatuisen arvokkaita ja fiksuja. Mutta osa niistä on väistämättä tässä kohtaa ennustellessa väärin, ja tulkinnat tulevat sen aikanaan todistamaan. Toivon kuitenkin että mahdollisimman pieni osa.

Miksi jotain kannattais tehdä?

Tietosuoja-asetuksen siirtymä-ajan päättyessä 2018, sovellukset jotka eivät tue uusia vaatimuksia ja käyttäjän oikeuksia, muodostavat merkittävän liiketoiminnallisen riskin yrityksille. Olemassaolevien sovellusten, järjestelmien, rekisterien ja infran osalta muutos on kivuliaampi. Helpompi on alkaa tutkailemaan ihannetilaa uusien sovellusten osalta, joita vielä vasta suunnitellaan tai rakennetaan, ja tulevaisuuden sovellusten osalta, joita tullaan rakentamaan jatkossa. Ajatuksena on, että aikaa myöten opitaan ottamaan tietosuojavaatimukset huomioon automaattisesti, ja niistä tulee yhtälailla sisäänrakennettu osa kaikkea tekemistä kuin esim. sovelluksen toimivuuden testauksesta eri selaimissa. Ja tätä myöden myös kuluja saadaan alas/opitaan laskemaan ne luonnolliseksi osaksi tekemistä ja testaamista.

Ok, tylsä johdanto. Terästetäänpä tätä toisin. Kun Harri H Hakkeri eräänä päivänä penetroi systeemin ytimeen, haluamme rajoittaa riskiä siitä miten paljon sakkoja tulee maksettavaksi, ja kuinka laajaa osaa asiakasrekisteriä joudut kontaktoimaan tietosuojamurron osalta, sekä millä sävyllä iltapäivälehdet repostelevat asiaa.

On väärinkäsitys ajatella myös, että tämä koskee vain suuria organisaatioita. Sen verran uskaltaudun ennustamaan tulevaisuutta, että muutamakin keskikokoinen paja tulee ajautumaan konkurssiin jos tietosuojan kanssa ollaan leväperäisiä tai peräti röyhkeitä. Tietämättömyys tai pää puskaan-strutsiefekti ei ole tässä puolustus tai suojautumiskeino, päinvastoin. Se että yrittää vaikkei 100% onnistuisikaan, lasketaan todennäköisesti eduksi ja sakkoja tai sakon uhkaa merkittävästi pienentäväksi tekijäksi.

TL;DR

No, tässä kuitenkin tiivistelmä, mitä tulisi huomioida sovelluskehityksessä vuonna 2017, ja projekti/järjestelmätasolla noin muutenkin. Jos muistan, rupattelen infratason ratkaisuista ja hallinnasta, ja organisaatiotason prosesseista lisää toisella kertaa.

  • Riskilähtöisyys: tunnista, minimoi, dokumentoi
  • Henkilötietojen esiprosessointi: Tunnista, minimoi, dokumentoi
  • Sama juttu 3. osapuolien riippuvuuksien osalta: tunnista, minimoi, dokumentoi
  • Sama juttu henkilötiedon prosessoinnin osalta myös sisäisesti: tunnista, minimoi, dokumentoi
  • Tiedon elinkaari: määrittele,minimoi, toteuta, dokumentoi
  • Varmuuskopiot ja arkistointi, huomioi kaikki edellämainitut
  • Testidata, huomioi kaikki edellämainitut
  • Testiympäristöt, tarkkana myös tämän kautta
  • Verkkoinfra, verkkosegmentointi, minimoidaan vahingot, maksimoidaan jäljet
  • Tietoaineiston suojaus: anonymisointi,pseudonymisointi,salaus
  • Käytön valvonta, monitorointi, hälytykset
  • Kyvykkyys osoittaa tehdyt toimenpiteet, ja prosessien toteutuminen
  • Murtautumistapauksessa: prosessien tuki, forensiikka, hälytykset
  • Tietoturva-ja muut päivitykset
  • CI- ja CD- toimitusputkien suojaus ja tietoturva
  • Huom. Myös availability, saatavuus, on tietosuoja-huolenaihe
  • Henkilön oikeudet uuden tietosuoja-asetuksen alaisuudessa, käytännön toteutus?

 

Oletusarvoinen tietosuoja tähtää aikalailla samaan asiaan kuin itse tietosuoja-asetuskin – riskipohjaiseen lähestymistapaan. Ja se taas tarkoittaa että matalan riskin järjestelmiä voidaan suojata keveämmin kuin korkean riskin järjestelmiä. Eli askel numero 1…

Tunnista henkilötiedot, tunnista riskit

Tietosuoja-asetuksen kannalta riskin näkökulma on vähän eri kuin yrityksen kannalta – mutta riskin kohde on silti sama, henkilötiedot. Aiemmin puhuttiin jo siitä, miten tietoa voidaan tunnistaa, luokitella. Järjestelmissä on paljon tietoa, joka ei ole tietosuoja-asetuksen mielessä kiinnostavaa, mutta on silti ollut – ja tulee olemaan – hyvin tarpeellista suojata, esim. tuotetiedot, strategiat, ennusteet. Se mitä ei tietosuoja-asetus määritä henkilötietoksi, on kuitenkin oman harkinnan mukaan edelleenkin vapaasti suojattavissa ja käytettävissä, miten halutaan. Henkilötiedon suojaamiseen taas löytyy GDPR suunnalta kättä pidempää.

Se, minkä voi rajata pois tietosuojalain tarkoittaman henkilötiedon piiristä, voidaan salata – tai jättää salaamatta – vapaasti miten halutaan.

Tietosuoja-asetuksen välineenä käytetään pitkälti DPIA analyysiä. DPIA, eli Data Protection Impact Analysis, on vaikutusten arvioinnin malli, jolla voidaan analysoida riskikulmaa. Ennestään käytössä on monessa yrityksessä ollut PIA, Privacy Impact Assessment, joka on tähdännyt samalle alueelle, mutta hieman laveammalla pensselillä. Molemmissa on ajatuksena määrämuotoinen ja kattava tarkastelu järjestelmässä kerättävälle tiedolle. Molemmat perustuvat riskien hallinnan ajatukseen. Eli analysoidaan kerättävän tiedon riskien taso, ja sen jälkeen, riskipohjaisesti, sovelletaan sopivia kontrolleja, kunnes riski saadaan laskettua hyväksyttävälle tasolle.

DPIA mallia ei tarvitse soveltaa joka järjestelmään, mutta jonkinmoinen esikarsinta olisi tietysti aina hyvä tehdä. Periaatteessa järjestelmän tavoiteltu tietosuojataso määräytyy aika yksinkertaisesti sen mukaan, mikä on sen sisältämä riskialttein yksittäinen tieto, eli vaikka muu järjestelmä olisi aika tylsä, jos yhdestä taulusta löytyy sairauspoissaolojen syitä, se määrittää aikalailla järeimmän kautta suojaustarpeen.

Tähän väliin on hyvä todeta oma lempiteesini. Sataprosenttista tietoturvaa ei ole. Sataprosenttista tietosuojaakaan ei ole. Jokainen järjestelmä voidaan murtaa, jokaiseen dataan päästä käsiksi, jos tarkastellaan asiaa rajattoman ajan, ja rajattoman budjetin suunnasta. Lisäksi tietojärjestelmät elävät mutatoituvassa maailmassa – järjestelmä joka on tänään 99% turvallinen, kokee muutoksia vuoden aikana, ja voi ensi vuonna ollakin vain 70% turvallinen, koska sen ympäristöön on vuoden aikana tullut uusia komponentteja, avauksia, muutoksia, tai vanhoista komponenteista on löytynyt haavoittuvuuksia. Yksi suurimmista tietoturva-haavoittuvuuksista on security misconfiguration, eli virhe ympäristöjen asetuksissa, ja niitä pujahtaa sisään helposti myös järjestelmiin jotka olivat luotaessa arkkitehtuurisesti kauniita ja täydellisiä. Kun huomioidaan prosessit, ympäristöt, teknologiat, muuttujia on vain niin paljon, ettei kukaan voi väittää hallitsevansa niitä kaikkia, nyt ja aina, ja vaikka istuisit palvelimen päällä haulikko kädessä vahdissa 24 tuntia vuorokaudessa, niin mörkö pujahtaa sisään heti jos herpaannut hetkeksi. Ja ensi vuonna voi olla että istut väärän palvelimen päällä.

Eli, ainoa oikea lähestymistapa on tunnistaa suurin riski, madaltaa sitä sopivin keinoin, ja jatkaa kunnes ollaan hyväksyttävällä riskitasolla. Lisäksi tätä ei voi tehdä kertarysäyksenä, projektina, vaan tietosuojan täytyy olla jatkuva prosessi.

Tai vielä mielummin sisäänrakennettu, läpinäkyvä osa prosesseja, kaikkea tekemistä. Siihen on vielä useimmilla yrityksillä pitkä aika. Valitettavasti monet ottavat lähtölaukauksen tekemiseen vasta ensi kesänä, kun uhat alkavat toden teolla realisoitumaan.

Tietosuoja-asetuksen kannalta erityisriskin muodostavia tietoja, joihin tulee kiinnittää erityistä huomiota, ovat mm.

  • Alaikäisistä rekisteröidyistä kerätyt tiedot (vaikka rekisteröity sittemmin olisi jo täysi-ikäinen)
  • Rotu,uskonto,politiikka,terveydentila,rikosrekisteri eli eiheet jotka monen mielestä eivät ole hyviä ruokapöytäkeskustelun aiheita (mutta omasta mielestäni niitä kiintoisimpia, moukka kun olen!)
  • Biometriset tunnisteet

Eli näitä tietysti suojataan rankimman kautta. Hyvä huomata kuitenkin, että ns kovan ja yksilöivän henkilötiedon ohella, myös identiteettiin liittyvää tietoa koskee moni tietosuoja-vaatimus, esim. henkilön oikeudet. Ja se mikä liittyy identiteettiin voi joissain tapauksissa olla yksilöivää, ’jos yksilöinti voi tapahtua kohtuullisella vaivalla tietoja yhdistelemällä’ – esim. muutama erillinen paikkatieto, tai erityinen tietopaketti harvaan asutulla alueella, jne – eli samalla lailla kuin itse identifioivaa tietoa, voi varautua suojaamaan myös identiteettiin liittyviä tietoja.

Toinen tapa lähestyä riskiä on miettiä ihan terveellä järjellä, miten suuren riskin REKISTERÖIDYLLE muodostaa, jos tämä tieto julkaistaisiin iltapäivälehden etusivulla. On hyvin tärkeää muistaa, että tietosuoja-asetuksen riskianalyysissä riskin näkökulma on aina rekisteröity, ei rekisterinpitäjä. Se näkökulma määrää, miten raskaasti tietoa tulisi suojata ja vartioida.

Toisaalta samoja periaatteita voi myös soveltaa muuhun sensitiiviseen tietoon, vaikka ei henkilötietoa olisikaan. Se ei ole tietosuoja-asetuksen kannalta mielenkiintoista, mutta voi olla liiketoiminnan kannalta hyvinkin mielenkiintoista.

Minimoi ja dokumentoi kerättävä tieto

No niin, nyt tulee se hyvä uutinen. Paras tapa keventää kuluja ja suojauskontrollien tarvetta, on olla keräämättä sitä henkilötietoa alunperinkään. Ennen vanhaan oli tapana keräillä kaikenlaista ihan vain varmuuden vuoksi, ties vaikka sitä jossain raportissa tai analyysissä kaivattaisiin, ja miksikäs ei. Sellainen lähestymistapa ei enää tietosuoja-asetuksen aikana kanna pitkälle.

Nyt kannattaa aloittaa miettimällä, mitä tietoa todella tarvitaan, ja millä oikeudella sitä kerään, varastoin, prosessoin. Tämä osa kannattaa dokumentoida hyvin, ja keskustella niistä harmaista alueista, joista ei olla varmoja. Voi myös miettiä sitä, voiko henkilötietoja eristää esim. omaan erilliseen järjestelmään, palveluun, kantaan, tauluun, jota voidaan suojata järeämmin kuin muita.

Sen ohella että minimoidaan siis ’leveyssuunnassa’ kerättävää tietoa – kyseenalaistamalla jo alun alkaen mitä kenttiä laitetaan, mitä tietoja kysellään – kannattaa myös huomioida aikadimensio, eli minimoida miten pitkään tietoja säilytetään. Minimointi ei tarkoita, että kaikki tieto pitäisi heti pyyhkiä, mutta tiedon elinkaaren suunnittelu on erittäin hyvä käytäntö ottaa mukaan tietomalliin alusta alkaen. Elinkaari voi olla vaikkapa puoli vuotta, tai viisi vuotta, kymmenen vuotta, jne. Mutta kun se ymmärretään, dokumentoidaan, ja toteutetaan, on taas pienennetty merkittävästi hyökkäyspinta-alaa.

Jos tapahtuu se pahin, eli huomataan tietomurto tai väärinkäytös järjestelmän ytimessä, muualle arkistoitu ja erikseen suojattu, tai täysin poistettu tieto voi olla sellaista, jota tietomurron ei katsota koskevan, ja näin ollen joudutaan kontaktoimaan vain niitä, joiden tiedot ovat aktiivisessa kannassa. Lisäksi tämäntyyppinen elinkaariajattelu ja suojauskontrollit lasketaan eduksi jos joudutaan jotain sakkorangaistuksia tai muita sanktioita miettimään.

Mutta siis, vanha tapa kerätä kaikki mahdollinen, ja säilyttää sitä ajan tappiin on huono käytäntö, ja sitä ei enää pitäisi harrastaa. Ja jos tiedon pinta-alaa tai säilytysaikaa ei pysty fiksusti minimoimaan, ainakin se kannattaa dokumentoida. Dokumentaatiota tulisi tuottaa sekä järjestelmäkehityksen ja ylläpidon tarpeisiin, että myös rekisteröidyn selosteeseen, jotta läpinäkyvyyden periaate toteutuu. Ja jos rekisteröity pystyy ymmärtämään kerätyn tiedon laajuuden, syyt, ja elinkaaren sen perusteella, pyynnöt esim. poistaa tietojaan järjestelmästä saattavat harventua.

Varaudu kuvaamaan MITÄ kerätään, MIHIN tarkoitukseen, ja MITEN sitä prosessoidaan (aka keiden toimesta). Varaudu tarjoamaan yhtä helpot keinot myöntää lupia tiedon keruuseen ja käyttöön, kuin lupien tarkistamiseen ja poistamiseen.

Minimoi pääsy

Tietosuojassa ei ole kyse vain ulkoisista tietomurroista, yli 30% väärinkäytöksistä tapahtuu verkon sisäpuolella, usein omien työntekijöiden, nykyisten tai entisten, toimesta. Huonosti suunnitellussa (tai monimutkaisessa, suurikokoisessa, iäkkäässä) verkossa voi olla myös pääsyä partnereilla, ulkoisilla työntekijöillä, konsulteilla, vuokratyöntekijöillä, tai miksei myös sovelluskehittäjillä, ylläpitäjillä, järjestelmätoimittajilla, jne.

Motiiveja voi olla esim. uteliaisuus, pyrkimys hyötyä, vandalismi, jne. Tietosuoja-asetus on tältä osin hyvin yksinkertainen. Henkilötietoon ei tulisi olla pääsyä enempää kuin mitä on tarpeen. Se mitä on tarpeen tulisi olla dokumentoituna. Eli ei ole kiellettyä että ylläpitäjillä on pääsy tietoon, kunhan se on läpinäkyvää. Eri asia on millaisen riskin se muodostaa.

Jos jotain tapahtuu, pääsyn minimointi myös voi rajoittaa vahinkoja, tai parantaa mahdollisuuksia näyttää toteen, mitä osaa tietoja tietomurto tai tietosuojaloukkaus koskee.

Näissä on hyvä suunnata huomiota terveydenhuollon järjestelmien suhteen, Suomessa ja muualla. Niissä on jouduttu jo iät ja ajat huomioimaan potilastietojen luottamuksellisuus, ja sieltä löytyy hyvin ideoita, käytäntöjä, toimintatapoja. Eri asia on, onko niitä terveydenhoidon järjestelmissäkään aina toteutettu ideaalisti. Mutta siellä on jouduttu jo pitkät ajat vastaamaan tällaiseen vaatimukseen.

3. osapuolen prosessointi

Tietosuoja-asetus ei estä sitä että dataa voi nyt ja jatkossakin prosessoida myös 3. osapuoli. Itse asiassa, prosessoijaksi kun lasketaan myös esim. pilvipalvelutarjoajat, kuten Amazon AWS, ja Microsoft Azure, enenevässä määrin toimitaan ympäristössä jossa on useampia osapuolia prosessoijina.

Nyt mennään alueelle joka ei ole omaa vahvinta aluetta: Lakipykälät ja niiden tulkinta. Mutta vapaasti oman ymmärryksen mukaan tärkeää on huomioida tämän osalta dokumentointi, ja sen mukana tulee luonnollisesti muut hyveet, kuten tiedon ja pääsyn minimointi ja suojaus, sekä sopimukset. Sopimustekniikka tulee varmaan tässä vielä kehittymään vuoden sisään, esim. Amazon on luvannut että ensi vuoden kesään mennessä tulee olemaan tietosuoja-asetuksen edellyttämät asiat huomioituna palvelusopimuksissa.

Se missä tulee olla tarkkana, on yhteisen vastuualueen kasvu, ja se missä rajat kulkevat. Moderni tietojärjestelmä on kokonaisuus jossa on aika monia tahoja. Jos nyt sattuisi että esim. AWS vastuunjakomallin mukaisesti tietosuojaloukkaus on tapahtunut 3. osapuolen tontilla, esim. medioiden huolimattoman hävityksen, tai fyysisen tietoturvan puutteen vuoksi, kantaako Amazon myös rahallista vastuuta sakoista, ja minkä firman liikevaihdon mukaan se menee? Entä jos 3. osapuoli ei olekaan AWS, joka sinällään on rutinoitunut ja ison talon koneisto, vaan autotallissa majaileva analytiikkafirma?

Mitä ajan takaa tässä, on pari huomiota. Tulkinnat tulevat elämään ja muotoutumaan vielä vuosia, sitä mukaa kun ikäviä asioita tapahtuu. Ja jatkossa ei riittäne pestä käsiään tietoturvasta ja tietoturvasta siinä kohtaa kun data siirtyy 3. osapuolen käsiin. Yhteisvastuullisuus on päivän sana, ja hyvä niin.

 

Jatkuu ensi numerossa…

No niin, kuten varoittelin, tästä tulee pitkä juttu. Kulmia on monia. Kirjailen lisää ajatuksia toisella kertaa, mennen lähemmäs kehittäjän konkretiaa. Osa näistä asioista, kuten 3. osapuolen sopimukset, eivät ole välttämättä heti toimittajan mielestä ehkä asioita joihin voi vaikuttaa, mutta softaprojektissa tulisi asianmukaisen matkaopastelijan kiinnittää huomiota siihen, miten asiat kokonaisuutena tehdään. Jos joku osa-alue on rempallaan, siitä kannattaa keskustella, ja kiinnittää huomiota, vaikka sieltä voisikin löytyä lisäkuluja.

Osittainen tietoturva on hyödytöntä tietoturvaa.

 

RESTAssured ja Istuntokeksit

REST ja istunnot ovat aikalailla paradoksi. Mutta silti joskus löytää itsensä tilanteesta jossa tilattomat REST palvelut ovat tilallisen autentikoinnin takana, esim. JSESSIONID evästeellä seurattuna (Ja kyllä, Suomen Kielitoimisto suosittaa nykyisin Cookielle termiä eväste, aiemmin keksi, ja uskokaa tai älkää, alunperin Taikapipari). Jos vielä heikommin käy, edessä voi olla joku single-signon hirviö joka tokenien sijasta käyttää useampaa istunto-evästettä. Ja jotenkin pitäisi saada kirjautumisen jälkeen istunnot säilymään.

Jos kyseessä on vain yksinkertainen JSESSIONID case, alkaen RestAssured versiosta 2 sille on tuki valmiina, parillakin eri tavalla. Nopein tapa saada sessio-cookie lähetettyä edestakaisin on tähän tapaan:

RestAssured.filters(sessionFilter);

Tuo pitää huolen, että sessio-eväste kaapataan vastauksista, ja liitetään pyyntöihin. Toimii heittämällä jos nimi on JSESSIONID – kunhan vain pidät huolen siitä, että sessionFilter on kaikissa kutsuissa sama, esim. luomalla sen staattisena:

public static final SessionFilter sessionFilter = new SessionFilter();

Entä jos evästeen nimi ei ole JSESSIONID? No, sen voi helposti muuttaa RESTAssured konfiguraatiolla:

RestAssured.config = newConfig().sessionConfig(new SessionConfig().sessionIdName("KOTLINSESSIONID"));

Entä jos pitäisi puljata useampia tracking cookieita, ja niiden nimet muuttuvat välissä? No ei hätää, sen voi hoitaa omalla custom filterillä, tähän tapaan:

public class CookieResenderFilter implements com.jayway.restassured.filter.Filter {

  private Map<String, Object> cookiesMap = new HashMap<String, Object>();

  @Override
  public Response filter(FilterableRequestSpecification filterableRequestSpecification,
                         FilterableResponseSpecification filterableResponseSpecification,
                         FilterContext filterContext) {
    filterableRequestSpecification.cookies(cookiesMap);
    Response response = filterContext.next(
      filterableRequestSpecification,
      filterableResponseSpecification);
    Map<String, String> cookiesReceived = response.getCookies();
    if (cookiesMap.size() == 0 && cookiesReceived.size() > 0) {
      for (String key : cookiesReceived.keySet()) {
        cookiesMap.put(key, cookiesReceived.get(key));
      }
    }
    return response;
  }
}

Ja tuo rekisteröidään käyttöön kuten yllä, taas pitäen huolta siitä että kyseessä on aina sama instanssi, ei aina uusi – koska sen jäsenmuuttujia käytetään tiedon varastointiin testisession ajan.

Oikeastihan tilallisuus + REST rajapinnat ovat helvetin seitsemännen tason keksintö ja jo tätä artikkelia kirjoittaessa tuli likainen olo, mutta aina ei pääse valitsemaan työkalupakkiaan. Kun pääsee, käyttäkää BCRYPT Token headereita, ihmiset!

Spring Boot Audit Logging

Jotain backendimpää taas vaihteeksi: Projekteissa tulee melkolailla tiheään vaadetta saada aikaan audit loggausta. Vaikkei tulisikaan, se antaa pitkän elinkaaren projekteissa itsellekin mielenrauhaa, että kykenee vastaamaan kysymykseen kuka teki mitä teki milloin teki (miksi teki ei vielä onnistu mutta ehkä IoT avulla sekin ratkaistavissa).

Audit loggausta voi tehdä villistikin eri tavoin ja eri vaatimuksilla. Joissain projekteissa on tultu nähtyä yksinkertainen audit service jota kutsutaan aina tarvittaessa, halutuista paikoista. Tässä on huonoa se, että pitää muistaa kutsua sitä, eli ei ole taattua että suuremmassa projektissa joka koodaaja on laittanut auditit paikalleen, lisäksi se rikkoo DRY periaatetta aika rumasti. Toisaalta on mahdollista tehdä monellakin tapaa filter/interceptor, joka tulee aina väliin ja loggaa vaikka kaiken. Mutta tässä mallissa voi olla ongelmana suuri hälyn määrä, eli voi olla että logi täyttyy tapahtumista jotka eivät ole oikeasti kiinnostavia mutta joita on paljon.

Kirjoittelen tätä blogia koska löysin mielestäni fiksun ratkaisun Spring Frameworkin puolelta, vieläpä Spring Boot yhteensopivana, eli ei xml:ää vaativana. Ratkaisu on fiksu koska se on mukava kompromissi kahdesta mainitusta ääripään tavasta – sisältäen tavallaan molempien huonoja ja hyviä puolia. Mutta ennenkaikkea se on melko kaunis, esteettinen, eikä riko yhtälailla ikävästi DRY periaatetta. Kirjaan näitä ylös myös ennenkaikkea itselleni muistiin, vähentää kivasti tarvittavaa aikaa soveltaa uudelleen, kun on tiedossa testattua luotettavaa ja (tällä hetkellä) ajantasaista tietoa.

Se mitä halusin on oikeastaan mahdollisuus auditoida metoditasolla on-demand, missä haluan. Ei täysautomaattisesti kaikkea, mutta ei myöskään samaa koodia copy-pasteillen joka paikkaan. Lisäksi halusin että voin halutessani määrittää audit eventille nimen, ja/tai kategorian, ja/tai koodin, pelkän metodi/luokannimen sijasta.

Homma lähtee liikkeelle ihan perinteisistä Spring AOP annotaatioista. Eli tarvitaan ensin Spring Boot projekti. Niistä olen kirjaillut jo aiemmin eli en lähde ihan sillä tasolla asiaa avaamaan tällä kertaa. Mutta sen päälle tarvitaan AOP dependency, näin:

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
 <version>${spring.boot.version}</version>
 </dependency>

Ja nyt ollaan jo aika pitkällä 😉 Hyvä huomata että Spring Boot on aika herkkä sille mitä kaikkea automatiikkaa olet kytkenyt päälle, itse olen saanut AOP featuret vahingossa joskus pois päältä esim. väärillä annotaatiolla Application/Configuration-luokassa. Mutta yleisin syy silti AOP toimimattomuuteen on rikkinäiset pointcutit. Joten testataanpa ensin iisisti mahdollisimman lavealla interceptorilla:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AuditAOP {
@After("execution(* *.*(..))")
 public void logServiceAccess(JoinPoint joinPoint) {
 System.out.println("AuditAOP: Completed : " + joinPoint);
 }
}

Jep, tuossa on AspectJ joinpoint joka tarraa kiinni ihan kaikkeen, niin kauan kuin mennään Springin läpi eli kohteena on Spring-manageroitu komponentti.Tässä kohtaa vain logataan joinpoint. Hyvä katsoa toimiiko, loggaako. Jos loggaa, erinomaista. Tarvittaessa Joinpointilta voidaan louhia lisääkin tietoja:

@After("execution(* *.*(..))")
public void logServiceAccess(JoinPoint joinPoint) {
  System.out.println("AuditAOP: Completed : " + joinPoint);
  Signature signature = joinPoint.getSignature();
  String methodName = signature.getName();
  String arguments = Arrays.toString(joinPoint.getArgs());
  System.out.println("Method: " + methodName + " with arguments "
    + arguments +  " has just been called");
}

Toimiiko tämäkin? Loistavaa. Nyt on sitten aika siirtyä itse pihviin. Voit nimittäin tehdä tästä annotaatiovetoista, annotaatiota voi käyttää halusi mukaan joko kääntämään auditin pois päältä, tai päälle. Itse tykkäisin että on annotaatio audit, jolla voin valita auditoitavan eventin nimen. Sen käyttö tapahtuisi näin:

@Component
class JokuRandomiSpringService {
  @Audit("ACCOUNT_DELETE")
  public void poistaPirunTarkeeTili() {
    // Jotain ihan järkyn fiksua koodia tähän kohtaan
  }
}

Jeah, aika mukava? Joten tehdään tämmöinen:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audit {
  String value() default "";
}

Sitten siihen todelliseen taikuuteen. Eli miten aop interceptor aktivoituu vain annotaation havaitessaan? Näin:

@Before("execution(* *.*(..)) && @annotation(audit)")
public void logServiceAccess(JoinPoint joinPoint, Audit audit) {
  String event = audit.value();
  if ("".equals(event)) {
    event = joinPoint.getSignature().getName();
  }
  Principal user = (Principal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  String remoteAddress = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
    .getRequest().getRemoteAddr();
  auditEventService.createEvent(new AuditEventEntity(user.getName(), event, remoteAddress));
}

Huomaa myös että annotaation mäpätään joinpointtiin muuttujanimellä, ja tulee parametriksi interceptorille. Annotaation sisältä voidaan kaivaa halutut parametrit, tässä tapauksessa value, joka olisi audit eventin nimi.

Tämän esimerkin koodi menee vähän pidemmälle. Jos nimeä ei ole annettu, oletusnimi on kutsuttavan metodin nimi, eli value on valinnainen. Lisäksi kaivellaan käyttäjän identiteetti security contextista, ja ip-osoite request contextista. Huom! Esitetty malli ei ole yksikkötestiystävällisintä, voi olla että on elegantimpiakin tapoja injektoida nämä contextit.

Mitäs vielä? Tuossa koodissa oleva auditEventService on ihan tavallinen Spring komponentti/service, jossa on yksi rivi koodia jolla talletetaan audit eventti kantaan, sopivaan tauluun, jossa on halutut sarakkeet. Samoin auditevententity on yksinkertaisesti Entity Object, jossa on kentät username, event, remoteaddress – id ja aikaleima ovat autogeneroituja. Lisätään tietoa sen mukaan mikä on paranoian taso.

Joskus tuli tehtyä sellaistakin järjestelmää jossa haluttiin mahdollisimman iisi tietoturva – yleinen tietoturvan sääntö kun on, että mitä tiukemmin kiristää käyttäjille näkyvää tietoturvaa, ja vaikeuttaa arkea, sitä luovemmin opitaan kiertämään se tietoturva, luoden usein jopa turvattomampi ratkaisu kuin alunperin (salasanoja muistilapuilla, sama salasana kaikkialla, kulunvalvottujen ovien availu kohteliaisuudesta, jne). Hyviä tietoturvaratkaisuja ovat eritoten ne systeemit joissa tietoturva ei hankaloita käyttäjän arkea. (Tämän takia salasanat ovat helvetistä)

Esim. tarkka auditointi tarkkojen roolilokeroiden sijasta, kaikki saavat tehdä lähes kaikkea mutta kaikesta jää jäljet. Tai jos haluaa niin molemmat päälle. Riippuu ympäristöstä mikä on fiksua, tarpeellista tai lainsäädännön sanelemaa.

Hyvä huomata että tämän tason auditointi ei loggaa virheitä jotka johtivat keskeytymiseen jo aiemmin ketjussa, eli jos haluat vielä laveammalla siveltimellä, voit täydentää esim. servlet tason filttereillä ja virhekäsittelijöillä.

 

Active Directory Autentikointi, LDAP, ja Glassfish

Toteutin aikanaan Active Directory LDAP pohjaisen autentikoinnin Glassfishillä pyörivään REST palveluun ja web sovellukseen, jossa on mahdollista käyttää Windows domain tunnuksia sisäänkirjautumiseen ja palveluiden käyttöön. Alunperin tehdessä homma toimi aika suorasukaisesti, AD ryhmän voi suoraan liittää Glassfish rooliin, mutta logiin jäi harmillista toistuvaa moskaa joka ei vaikuttanut toimintaan mutta silti ihmetytti. Kyseessä oli tällainen virheilmoitus:

Caught exception. javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name ’DC=mycompany,DC=fi’

Tästä samasta oireesta tuntui kärsivän puoli Internettiä, vailla ajatuksia ja vastauksia miten ratkoa tämä. Tuntui olevan jonkun asteinen verkko-infra oire.  Ohjeistus oli monesti käntää ympäristöasetus java.naming.referral asentoon ignore – tai follow. Kumpikaan ei auttanut oireeseen tässä tapauksessa.

Viime viikolla löysin viimein korjauksen joka toimi, eli ongelman sai poistumaan yksinkertaisesti vaihtamalla porttia jota käytetään AD:n kanssa keskusteluun, vanhsta portista 389, jota useimmat ohjeet suosivat, uuteen porttiin 3268 (Global Catalog) – josta voi tehdä hakuja seuraamatta automaattisesti kaikki linkkejä. Tässä siis toimiva esimerkki Glassfish 4 AD autentikoinnista:

<auth-realm name="TieturiActiveDirectory" classname="com.sun.enterprise.security.auth.realm.ldap.LDAPRealm">
    <property name="directory" value="ldap://ournameservername:3268"></property>
    <property name="base-dn" value="DC=mycompany,DC=fi"></property>
    <property name="jaas-context" value="ldapRealm"></property>
    <property name="assign-groups" value="mygroup"></property>
    <property name="search-filter" value="(&amp;(objectCategory=user)(sAMAccountName=%s))"></property>
    <property name="search-bind-dn" value="mylogin"></property>
    <property name="search-bind-password" value="mypassword"></property>
    <property name="group-search-filter" value="(&amp;(objectCategory=group)(member=%d))"></property>
</auth-realm>

Tärkeä avain tuossa yllä on myös assign-groups arvo, se määrittää mihin Glassfish ryhmään autentikoidut käyttäjät tuitataan. Ryhmän voi taas mäpätä haluamaansa rooliin. Parametreja säätämällä lisää voi tarkentaa lisää hakuehtoja joilla suodatetaan hyväksyttyjä tunnuksia tiukemmin.

Kannattaa käyttää tuossa filtterissä objectCategory-parametria objectGroup:in sijaan, koska objectCategory on indeksoitu ja nopeampi. Oikeasti kannattaa myös suodattaa disabled-tilassa olevat tunnukset pois, esim. näin:

<property name="search-filter" value="(&amp;(objectCategory=user)(sAMAccountName=%s)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"></property>

Tarina perustuu tositapahtumiin. Luonnollisesti kaikki nimet ja ip-osoitteet ovat muutettuja ja yksinkertaistettuja jotta asianomaisia suojellaan riittävästi.

Tiivistettä Javalla

Kauan aikaa sitten kouluttelimme kurssia Java Security. Jostain syystä se ei ollut koskaan suuri hitti, niihin aikoihin tietoturva kuten testauskin oli harvojen herkkua, koska molemmat tehtiin projektien lopussa vähäisellä prioriteetilla, pienikin viivästys aiheutti sen että karsitaan tietoturvasta ja testauksesta. Ja siksi harvoja kiinnosti miten Java-kielessä voi kryptata sisältöä raskaillakin algoritmeilla tai miten dataan voi tehokkaimmin laskea tiivisteitä tai enkoodata tai dekoodata sitä esim. base64 muotoon.

Rajapinnat olivat kuitenkin suorastaan hauskoja ja helppoja, joten on vähän sääli että niitä ei ole päässyt sen enempiä käyttelemään itsekään. Pitkästä aikaa törmäsin artikkeliin jossa käsiteltiin näitä, ja ajattelin verrytellä koodiesimerkin toimivaan kuntoon. Nykyään tietoturva alkaa taas pakostakin olemaan enemmän tapetilla, ja luottamuksellisuus ja kiistämättömyys voivat olla taas tavoiteltavia asioita.

Eli tässä koodipätkä itselleni muistiin, miten Java-kielen valmiilla piirteillä voi laskea datasta sha-1 -tiivisteen, ja samaan syssyyn Base64-koodata sen siirtoa tai talletusta varten:

public class MessageDigestTest {
    public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        String sourceString = "HelloWorldTextToDigest";
        System.out.println("Source text:" + sourceString);
        String returnString = "";
        MessageDigest messageDigester = MessageDigest.getInstance("SHA-1");
        messageDigester.update(sourceString.getBytes("utf8"));
        returnString = new String(Base64.encode(messageDigester.digest()));
        System.out.println("Base64 encoded SHA-1 digest of source string:" + returnString);
    }
}

Pari huomioitavaa asiaa koodiesimerkistä:

  • Base64 on nykyisellään Javassa ei-standardi HotSpot myllyn sisäinen toteutusluokka, niinkin mehukkaassa paketissa kuin com.sun.org.apache.xerces.internal.impl.dv.util.Base64 – eli sen läsnäoloon tai rajapinnan muuttumattomuuteen ei voi luottaa
  • Java 8 tuonee mukanaan tästä standardipaketti-version, jossa rajapinta pysyy samana ja joka näin ollen löytyisi kaikista virtuaalikoneista aikanaan, Base64 muuttuu siis helpommaksi. Java 8:ssa Base64-luokka löytyy paketista java.util. Aiemmille toteutuksille voi olla hyvä käyttää jotain 3rd party kirjastoa jota voi itse hallita
  • MessageDigest taas on sisäänrakennettu hyvinkin vanha luokka. Sen kautta voi käyttää mitä hyvänsä Javalle saatavilla olevia alghoritmeja, nykyisiä ja tulevia, tiivisteen laskemiseen. Algoritmeja löytyy esim. SHA-1, SHA-256, SHA-384, SHA-512, MD2 ja MD5.
  • Samaan tapaan Java-kielessä toimii myös luokka nimeltä Cipher, jolla voi kryptata sisältöä. Sekin luodaan ensin, sitten update toiminnolla dataa sisään, ja lopuksi kryptattu versio ulos. Tähän tulee mukaan myöa avaintenhallinnan rajapinnat. Nykyisellään Cipher tukee algoritmeja kuten AES,AESWrap,ARCFOUR, DES,DESede, RSA, mutta myös esim. Blowfish, ECIES (Elliptic Curve Integrated Encryption Scheme), ja lukuisia muita. Jos ne eivät riitä, lisäalgoritmeja pystyy asentamaan. Noista mainituistahan saa nykypäivänä unohtaa heti osan algoritmeista, koska niitä ei pidetä enää turvallisena.
  • Pienenä haasteena jenkkilässä rinnastetaan kryptografiaa aseisiin ja ammuksiin, ja järeiden avainvahvuuksien algoritmeilla on rajoituksia maastaviennin suhteen. Tämän vuoksi oletus-Java asennuksessa avainten maksimivahvuus on rajoitettu ja avatakseen täyden vahvuuden avaimet täytyy asentaa virtuaalikoneeseen lisäpalikkaa. Tai kirjoittaa pieni koodihäkkäys joka poistaa eston.
  • Lisätietoa algoritmeista esim. http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest

Java EE tietoturva

Törmäsin hyvään esitykseen turvallisesta Java EE rakentamisesta.  Ei mitään uutta eikä mullistavaa, mutta samoja hyviä käytäntöjä joita itsekin allekirjoitan:

http://www.java-tv.com/2012/12/04/java-ee-security-in-practice-with-java-ee-6-and-glassfish/

Kyllähän tiedetään mikä tekee ratkaisusta turvallisen. Silti häkellyttävää miten paljon turvatonta koodia on liikenteessä. Tietoturva on usein se asia joka jää hakkuupölkylle yhdessä testauksen kanssa kun tulee kiirettä.

Tähän voi tulla muutos. Briteissä on jo ollut pari vuotta voimassa säädös jonka nojalla yritystä voidaan sakottaa tietomurroista jotka ovat johtuneet huonosta tietoturvasta järjestelmissä – sakko voi nousta 500,000 puntaan asti. Tietomurtoja ovat esim. tilanteet jossa asiakkaiden salasanoja tai henkilö tai maksutietoja joutuu vääriin käsiin huolimattomuuden tai haavoittuvuuden vuoksi. EU puuhastelee lainsääsäntöä (General Data Protection Regulation) jossa rikkeestä voi päästä maksamaan sakkoa 2% yrityksen vuotuisesta liikevaihdosta – tai litteän miljoonan. Auts!

Kun tietoturva nostaa päätään myös Open Web Application Security Project OWASP on nousussa, antaen vähän kättä pidempää tietoturvahaavoittuvuuksien tunnistamiseen ja koulutuksen formalisointiin. OWASP on hyvin sovellettavissa esim. Java web hankkeisiin tai Scala tai Grails projekteihin.

Lähde:

http://ec.europa.eu/justice/data-protection/document/review2012/com_2012_11_en.pdf

http://en.wikipedia.org/wiki/OWASP

Java vs HTML 5 vs Flash

Olen pannut huvittuneisuudella merkille miten maailmalla kirjoitetaan artikkeleita Javan tuhosta. Alustalla on tietoturvahaavoittuvuuksia, se suositellaan siis poistettavan. Näin ollen Javan aika on ohi ja se vuotaa rahaa Oraclelta. Tässä lauseessahan on niin paljon väärin ettei tiedä mistä aloittaa. Javan pääkäyttöhän ei ole työasemissa vaan servereissä, jonne mm. IBM, Oracle ja RedHat ovat tehneet ja tulevat tekemään rahallisia panostuksia, ja joissa Java-pohjaiset web serverit ovat tyypillisimpiä alustoja web-sovelluksille, etenkin yrityspuolella. Ja jossa noilla mainituilla haavottuvuuksilla ei ole (juuri) mitään merkitystä. Java siis elää tai kuolee sen mukaan miten hyvin se suoriutuu palvelinohjelmoinnissa vastaan muita teknologioita, ja miten hyvin sitä puolta tuetaan ja kehitetään. Jos tänä päivänä java se tapettaisiin kokonaan, merkitys Javan kannalta ei olisi suuren suuri. Javan merkitys on servereissä, ei työasemissa. Tosin työasemissakin sekä mobiililaitteissa on Javaa asenneltuna miljardeja yksi

Väite: Pitäisikö sitten Java työasemista poistaa? Sehän on täynnä tietoturva-aukkoja ja joka kuukausi eletään pelossa löytyykö uusi. Lisäksi, kuka enää tekee rich client ohjelmointia, kun kaiken voi tehdä HTML5:lla? Adobekin luopuu flashistä. Java on ihan tarpeeton tekele?

Vai onko? Katsotaanpa tarkemmin näitä argumentteja:

Voiko kaiken todella tehdä HTML5:lla? Kenties voi. Perinteisesti rikasta käyttöliittymää lähdettiin tekemään Javalla tai Flashillä siksi että perinteinen HTML pohjainen web-sovellus on staattinen ja jäykkä, se reagoi vain napinpainallukseen ja pystyy ainoastaan näyttämään web sivuja ja ottamaan syötettä, ei esim. minkäänlaista pääsyä työaseman/mobiililaitteen rautaan kuten älykortinlukijoihin, (web) kameraan, paikkatietopalveluihin, yhteystietoihin, kalenteriin, värinärajapintaan, kiihtyvyysanturiin, kallistusanturiin, verkkokorttiin, äänikorttiin, piirtämiseen, videon soittoon, matalan tason tehokkaisiin grafiikkarajapintoihin, jne. JavaScript laajensi palettia antamalla mm. rikkautta tapahtumankäsittelyyn. AJAX lisäsi vielä lisää rikkautta kun dataa voi hakea dynaamisesti verkon yli ja sillä päivittää sivua ilman että täytyy edes navigoida. Ja HTML 5 lisää vielä lisää kuten Canvas piirtoalusta, ääni ja videorajapintoja, paikallinen tietovarasto. Sillä voi soitella videota ja ääntä, ja tehdä kaunista käyttöliittymää.

Mitä sitten uupuu? No pari asiaa. HTML 5 ei pärjää suorituskyvyssä lähempänä käyttöjärjestelmää ajetulle koodille, eli kun halutaan ottaa koneesta kaikki irti, se jää jälkeen. HTML5 pääsee vain niihin resursseihin käsiksi jotka on siihen avattu, eli aina kun joku uusi vimpain keksitään työasemiin tai mobiililaitteisiin, HTML5 sovellukset ovat jälkijunassa sen soveltamisessa. Samoin tietysti Java, mutta vähemmän kuin HTML. Esim. JavaFX teknologian suorituskyky on täysin eri planeetalta ja visuaalista ilmaisukykyä riittää eri sarjassa.

Pahimmat niitit tulevat kuitenkin tässä: HTML5 ei ole vielä edes valmis, se on tällä hetkellä vaporwarea kunnes toisin todistetaan. Sen spesifikaatiokaan ei ole valmis, se _ehkä_ tulee 2014. Selainvalmistajat _ehkä_ toteuttavat sen. Siihen mennessä _ehkä_ maailma ei ole muuttunut yhtään. Muistan myös 90-luvun suuret selainsodat, kun Dynamic HTML ja JavaScript olivat uusi ja kuuma juttu, ja valmistajat kilpailivat siitä kuka tekee hienoimmat toteutukset ja tulkitsee standardeja nerokkaimmin ja mahdollisimman epästandardein tavoin. 2014 web sivustot tulevat räjähtämään käsiin ellei ihmiskunta ja eritoten selainvalmistajat ja kehittäjät ole jotenkin viisastuneet siitä. Todennäköisesti joudutaan taas tekemään eri versiot sivustoista eri selainversioille, tätä on jo nyt näkyvissä vaikka speksi ei ole edes valmis.

Entäpä sitten tietoturvaongelmat? Javassahan on niitä, haavoittuvuuksia, jotka pitää paikata tai niitä hyödynnetään. Samoin on Windowsissa. Linuxissa. JavaScriptissä. Ja niin myös Ajaxissa ja eritoten HTML 5:ssa. Jos haluaa alustan jossa ei ole tietoturvaongelmia niin se onnistuu kunhan alustassa ei voi tehdä mitään. Mitä enemmän alustalla voi tehdä sitä rajummin siellä on potentiaalia tietoturva-aukoille, ja aukotonta järjestelmää ei olekaan. Miten nopeasti tulevat HTML5 aukot paikataan ja kenen toimesta?

No, siinä pohdittavaa. HTML5 on mielestäni mielettömän kaunis ja lupaava teknologia, mutta se ei ole mullistava eikä sovelluskehityksen pelastaja eikä missään nimessä ongelmaton. Siinä tulee olemaan tietoturvaongelmia ja vakavia sellaisia kun sen käyttö lisääntyy, samalla kun sen ominaisuudet muuttuvat paremmiksi. Suuren vallan mukana tulee suuri vastuu.

Monet artikkelit ja mielipiteet julistavat sokeasti HTML5:den uutta tulemista ja kaikkien rikkaiden käyttöliittymäteknologioiden kuten Flash, Applet, JavaFX kuolemaa, ja tämä on ehkä yksipuolinen näkemys. En halua myöskään väittää että JavaFX jyräisi HTML5:sen, tuskinpa vain. Hyvä jos sille tilaa riittää markkinoilla. Mutta HTML5 ei ole Suuri Pelastaja joka mullistaa kaiken. Se on yksi teknologia muiden joukossa, ja omaa samoja ongelmia kuin muutkin. Suorituskyky, pääsy uusiin rautaratkaisuihin ja rajapintoihin client päässä, todellinen siirrettävyys eri alustoilla kuten eri selaimet, käyttöjärjestelmät, ja eri valmistajien mobiililaitteet, sekä tietoturva, ovat kaikki haasteita, jotka tulee ratkaista. Yhtäkaikki ei ole huono juttu että 2015 vuonna työkalupakissa on useita hyviä uusia mahdollisuuksia.

Lähdelinkkejä ja lisäluettavaa:

Facebook liian aikaisessa HTML5:sen kanssa:
http://css.dzone.com/articles/facebook%E2%80%99s-html5-mistake

HTML5 suorituskykyä voidaan kiihdyttää:
http://www.html5rocks.com/en/mobile/nativedebate/

Green is good 😉
http://en.wikipedia.org/wiki/Comparison_of_layout_engines_(HTML5)

Java vs HTML5 Canvas:
http://www.zynaps.com/site/experiments/mandelbrot.html