Sessioteoriaa

Mureakuha

Loikkaa: valikkoon, hakuun

by pikkumyy

Sisällysluettelo

PHP:n oma sessiojärjestelmä

PHP:n oma sessiojärjestelmä toimii jokseenkin samalla tavalla kuin ASP:llä varustetun IIS-palvelimen järjestelmä. Käyttäjälle siis annetaan eväste, oli sille tarvetta tai ei. Vaikka tämä tuntuu jotenkin epäoleelliselta seikalta, niin homma vie hieman tehoja, muistia ja levytilaa. Huomattavasti parempi järjestelmä on antaa istuntoeväste vasta, kun sen antaminen on tarpeellista, toisin sanoen käyttäjän kirjauduttua sisään, lisättyä tuote ostoskoriin jne.

Itse hakkasin päätä seinään pitkään PHP:n oman systeemin kanssa, kunnes päätin unohtaa sen. Vaikka sen suosimiselle on hyviä syitä kuten se, että kielen oma natiivi sessiojärjestelmä on todennäköisesti toimivampi ja testatumpi kuin Keke Kooderin itse tekemä sekä PHP:n olemassaolo luo jo vaadittavat ohjelmalliset valmiudet sen käyttöön (toisin kuin esimerkiksi sessiojärjestelmä, jossa käytetään tietokantaa), eivät nämä hyödyt kompensoi haittoja.

Sessioiden periaate

Sessiot ovat suomeksi istuntoja. HTTP-protokolla on yhteydetön. Palvelin ei pidä yhteyttä asiakkaisiin ja sivunlatauksien välillä ei palvelimen päässä säilytetä tietoa lataajista. Sivun latauksen loputtua yhteys palvelimeen katkaistaan. Joissain tapauksissa yhteyttä palvelimen ja asiakkaan välillä voidaan pitää hengissä, mutta käytännössä HTTP-yhteyden laatu on silti yhteydetön. Jokainen sivunlataus yhtä uusi tapaus palvelimelle kuin ensimmäinenkin. Sessioilla tarkoitetaan siis yhden tietyn käyttäjän (tai käyttäjäryhmän) usean sivunlatauksen pituista istuntoa.

Yleensä sessioihin liittyy sessiotunniste, sessioid. Tämä luodaan palvelimen päässä ja jatkossa käyttäjä tunnistetaan tällä tunnisteella. Käytännössä sessiotunniste voidaan antaa GET-kutsuina ja lisätä jokaiseen linkkiin sivunlatauksen yhteydessä. Tämä on hyvin toimiva tapa, mutta sisältää riskejä. Kun kopioit linkin kaverillesi, saattaa tämä linkki sisältää sessioid:si, ja kaverisi klikattua linkkiä palvelin tunnistaa kaverin sinuksi. Toinen ja huomattavasti käytetympi tapa on antaa sessioid evästeenä eli cookiena. Näin palvelimen ja selaimen keskustelu tapahtuu käyttäjästä katsoen 'piilossa', mutta ei välttämättä toimi kaikissa selaimissa (vaikkakin kyllä kaikissa moderneissa graafisissa selaimissa, ellei käyttäjä sitä erikseen ole estänyt).

Käytännön teoriaa

Kun tarve vaatii käyttäjän tunnistamista, luodaan yksilöllinen käyttäjätunniste. Tähän EI kannata käyttää mitään käyttäjälle yksilöllistä dataa, koska se heikentää tietoturvaa (ja tekee tunnisteesta helpommin arvattavan). Tämän luomiseen PHP:ssä on ääretön määrä tapoja, mutta mm. funktiot uniqid(), md5(), mktime() sekä random() ovat hyviä tähän käyttöön. Mitä suurempi avaruus mahdollisissa sessiotunnisteissa on, sitä parempi. Jos siis käytät sessiotunnisteena lukuja 0-65536, on kaverisi sessio kaapattu (session hijacking) muutamassa sekunnissa ellei sinulla ole muita tietoturvatarkistuksia. Kannattaa muistaa, että monimutkaiset tavat luoda uniikki tunniste eivät välttämättä ole parempia kuin yksinkertaiset. Esimerkiksi md5(random(0,mktime())) näyttää kivalta, mutta loppujen lopuksi käyttäjän loggausaika on arvattavissa, jolloin mktime():n palauttama arvo voidaan arvata sanotaan nyt vaikka 3600:n (tunti sekunteina) tarkkuudella. Näin randomin palauttama luku voidaan arvata, ja md5():n siitä suoltama merkkijono bruteforcettaa. Kannattaa lukea PHP:n sivuilta uniqid:n dokumentaatio, sieltä löytyy muutamia hyviä tapoja luoda uniikkeja, vaikeasti arvattavia merkkijonoja. Ja mikäpä estää luomasta muutamaa merkkijonoja eri tavalla ja muodostaa niistä yksi iso uniikki merkkijono. 128 merkkiä pitkä tunniste on huomattavasti parempi kuin 16 merkkiä (16 merkkiä pieniä ja isoja kirjaimia ja numeroita tarjoaa noin triljoona triljoonaa (luvussa 28 nollaa) vaihtoehtoa. Ei kuitenkaan kannata antaa suurien numeroiden hämätä.)

Annettuasi uniikin tunnisteen käyttäjälle, tulee se tallentaa jonnekkin. Tallentaessasi sessioid:tä, kannattaa varmistaa, että se todellakin on uniikki. Käy siis katsomassa tietokannastasi onko kyseinen tunniste jo käytössä. Tämä on todella helppo unohtaa, ja vaikka mahdollisuudet saada sama sessioid olisivat hyvin hyvin pienet, on pienikin mahdollisuus huomattavasti suurempi kuin täysi mahdottomuus :) Mikäli annat sessioid:n evästeenä, ei kannata kiinnittää sen vanhentumiseen ihmeempiä huomioita. Käyttäjän selainohjelma vanhentaa evästeen, jolloin se on riippuvainen käyttäjän kellon asetuksista. Mikäli käyttäjän kello on vuoden jäljessä, vanhenee tunnissa vanheneva eväste todellisuudessa vuoden ja tunnin kuluttua. Mikäli taas vuoden edellä, vanhenee se heti sen saamisen jälkeen. Kannattaa antaa siis joko session pituisia evästeitä (jotka vanhenevat selainta suljettaessa), tai hyvin pitkän ajan (vaikka 32 vuotta) evästeitä. Tietoturvallisesti tällä ei ole ihmeempää merkitystä, vaikka periaatteessa sessiocookiet ovat parempia, koska niiden pitäisi poistua selaimen suljettua - niitä myös useammin käytetään.

Käyttäjälle ei yleensä kannata lähettää muita tietoja sessioid:n lisäksi, vaan ne kannattaa tallentaa tietokantaan ja viitata eli referoida niihin kannan sisällä. Näin sivunlatauksessa saadaan ja lähetetään vähemmän tietoa (jolloin se nopeutuu), tiedon rakenne pysyy paremmin aisoissa (käyttäjä ei itse pääse muokkaamaan istuntoon liitettyä tietoa) ja tarvittavat tiedot tulee haettua vasta kun on tarve, verrattuna siihen, että ne saataisiin käyttäjän selaimelta jokaisen sivunlatauksen yhteydessä, oli tarvetta tai ei. Ostoskorin sisältö, käyttäjän IP, mitä tahansa tietoa halutaankaan istuntoon tallentaa, on sen paikka parempi kannassa.

Sessioid:n käytön teoria

Käyttäjän kirjauduttua sisään / lisättyä tavaroita ostoskoriin / tms. eli kun hänelle on annettu sessioid, tulee seuraavaksi eteen sen käyttö. Käyttäjä lataa seuraavan sivun ja lähettää sessioid:nsä sen yhteydessä.

Ensimmäisesi tulee tarkastaa, että sessioid on olemassa, ja että sen antamisesta tai viimeisestä käyttökerrasta ei ole kulunut tiettyä aikaa. Jos käyttäjä kirjauduttuaan sisään lataa seuraavaa sivua viiden sekunnin päästä, lähettää hän täsmälleen samat tiedot, kuin mitä hän lähettää ladatessaan sivua viiden vuoden päästä. Mikäli edellisestä sivunlatauksesta on kuitenkin viisi vuotta, on epätodennäköistä, että käyttäjän sessioid:hen voi enää luottaa. Toisinsanoen vaikka sessioid on oikea, se hylätään ja käyttäjä heitetään takaisin kirjautumisruutuun. Mikäli sessioid on väärä (sitä ei löydy kannasta) annetaan käyttäjälle tietenkin sama kirjautumisruutu. Tämän lisäksi on muita seikkoja, joiden vaihtumiseen kannattaa kiinnittää huomiota: onko käyttäjän IP vaihtunut latausten välillä? Entä selain? Jos on, niin voi olla syytä hylätä käyttäjän lähettämä tunniste ja näyttää se kirjautumisruutu.

Koska kannassa ei tietenkään kannata pitää ikuisuuksiin vanhentuneita sessioid:tä ja niihin referoivia sessiotietoja, kannattaa aina välillä (tai vaikka jokaisen sivunlatauksen yhteydessä) ajaa delete-query, joka poistaa vanhentuneet rivit sessio-tauluista.

Jotkut järjestelmät suosivat käyttäjätietojen tallentamista sessio-tietoihin. Tämä voi tuoda merkittävän nopeusedun verrattuna järjestelmiin, joissa käyttäjätiedot (ja oikeudet) haetaan erikseen joka kerta niille varatuista omista tauluista. Huono puoli asiassa taas on se, että käyttäjille tehdyt muutokset tulevat voimaan vasta, kun käyttäjä seuraavan kerran kirjautuu sisään. Sama pätee ostoskoreihin: tietojen, kuten ostoskorin sisällön, tuotteiden hintojen, saatavuuden (varastosta) yms. hakeminen voi viedä huomattavasti pidemmän aikaa kuin että se saataisiin nopeasti yhdeltä riviltä kannasta. Tietenkin kun ostoskori on täynnä ja tuotteita mennään tilaamaan, on helppo varmistaa, että ostoskorin sisältö todellakin on mahdollinen, joten tässä tapauksessa ongelma ei ole yhtä suuri kuin esimerkiksi siinä, että käyttäjä olisi poistettu kannasta, mutta sessiotiedoissa hän on yhä olemassa. Kannattaa muodostaa oma toiminnallisuus tarpeiden mukaan.

Käyttäjän kirjauduttua ulos tuhotaan sessioid ja kaikki viittaukset siihen. Tämän toiminto yhdessä istunnon vanhentumisen (session timeout) kanssa takaa tietoturvan tietyllä tasolla. Jos käyttäjä siis painelee takaisin-nappulaa uloskirjautumisen tai session vanhentumisen jälkeen ja tekee jotain joka vaatii sisäänkirjautumisen, tulee *ennen* mitään toimenpiteitä tarkastaa, että käyttäjällä todellakin on voimassaoleva sessioid. Käytännössä siis jokaisen sivun alussa tarkastetaan, että sivu saadaan ladata (jos ei, niin tulostetaan kirjautumisruutu ja tapetaan sivunlataus).

Henkilökohtaiset työkalut