Kirjautuminen

Haku

Tehtävät

Koodit: PHP: Salasanojen käsittely

Kirjoittaja: Metabolix

Kirjoitettu: 20.12.2012 – 22.06.2013

Tagit: ohjelmointitavat, tietoturva, koodi näytille, vinkki

PHP:hen on vihdoin lisätty helpot funktiot salasanan tiivisteen laskemiseen ja salasanan tarkistamiseen. PHP:n oma menetelmä on turvallisempi kuin monesta vanhasta koodista tuttu yksinkertainen MD5, ja se on myös suunniteltu niin, että sitä on helppo käyttää.

Uudet funktiot lisättiin PHP:hen versiossa 5.5. Vanhempiin versioihin 5.3.7:stä alkaen ne voi ladata GitHubista ja liittää mukaan include-komennolla.

Salasanan asetus ja tarkistus

Funktio password_hash on tiivisteen laskemista varten silloin, kun käyttäjä rekisteröityy tai vaihtaa salasanansa. Se huolehtii automaattisesti salasanan turvallisesta suolaamisesta ja sopivasta tiivistealgoritmista. Tietokantaan pitää tallentaa vain tiiviste, ei koskaan salasanaa.

$tiiviste = password_hash($salasana, PASSWORD_DEFAULT);

Funktio password_verify tarkistaa käyttäjän salasanan. Sille annetaan syötetty salasana ja aiempi tiiviste ja se kertoo, onko salasana oikea. Aiempi tiiviste pitää siis ensin hakea tietokannasta.

if (password_verify($salasana, $tiiviste)) {
	// Salasana on oikein.
}

Funktio password_needs_rehash kertoo, pitäisikö salasanan tiiviste laskea tietokantaan uudestaan. Tarkistus pitää tehdä kirjautumisen jälkeen, ja tarvittaessa tiiviste pitää laskea uudestaan ja tallentaa. Tällä on merkitystä tulevaisuudessa, kun PHP:n asetukset muuttuvat ja näitä funktioita vahvistetaan uusilla algoritmeilla tai muilla tavoin.

if (password_verify($salasana, $tiiviste)) {
	// Salasana on oikein. Tarkistetaan, pitääkö päivittää.
	if (password_needs_rehash($tiiviste, PASSWORD_DEFAULT)) {
		$tiiviste = password_hash($salasana, PASSWORD_DEFAULT);
		// TODO: Tallenna uusi tiiviste tietokantaan!
	}
}

Esimerkki: Käyttäjä-luokka

Tässä toteutetaan osa kuvitteellisesta Käyttäjä-luokasta. Käyttäjän hakemisen ja tallentamisen joudut tekemään itse!

class Käyttäjä {
	public $tunnus;
	public $tiiviste;

	public function asetaSalasana($salasana) {
		// Tiiviste lasketaan funktiolla password_hash.
		$this->tiiviste = password_hash($salasana, PASSWORD_DEFAULT);
	}

	public function tarkistaSalasana($salasana) {
		// Salasana tarkistetaan funktiolla password_verify.
		if (!password_verify($salasana, $this->tiiviste)) {
			return false;
		} else {
			// Kirjautuminen ok, päivitetään vielä tiiviste tarvittaessa.
			if (password_needs_rehash($this->tiiviste, PASSWORD_DEFAULT)) {
				$this->asetaSalasana($_POST["salasana"]);
				$this->tallenna();
			}
			return true;
		}
	}

	// Nämä toiminnot joudut tekemään itse:
	public static function hae($tunnus) {
		throw new Exception("Käyttäjä::hae puuttuu!");
	}
	public function tallenna() {
		throw new Exception("Käyttäjä::tallenna puuttuu!");
	}
}

Rekisteröityminen tapahtuisi näillä funktioilla suunnilleen näin:

$käyttäjä = new Käyttäjä();
$käyttäjä->tunnus = $_POST["tunnus"];
$käyttäjä->asetaSalasana($_POST["salasana"]);
$käyttäjä->tallenna();

Käyttäjän kirjautuminen tarkistettaisiin tähän tapaan:

$käyttäjä = Käyttäjä::hae($_POST["tunnus"]);
if ($käyttäjä == null) {
	throw new Exception("Tunnusta ei löydy!");
} elseif (!$käyttäjä->tarkistaSalasana($_POST["salasana"])) {
	throw new Exception("Väärä salasana!");
} else {
	// Kirjautuminen hyväksytty, mitä nyt?
}

Kommentit

Arttut02 [22.12.2012 13:43:48]

#

Pieni sivukommentti tuohon käyttäjän kirjautumisen tarkistamiseen. Käyttäjälle itselleen ei kannata ilmoittaa erikseen, että salasana on väärä. Tämä yleensä paljastaa sen, että käyttäjätunnus on kuitenkin ollut oikea. Sen jälkeen voidaan kyseisen käyttäjätunnuksen salasanaa ruveta kaivamaan esille omilla konsteillaan. Parasta on aina ilmoittaa, että esim. että "kirjautuminen epäonnistui". Näin saadaan edes vähän vaikeutettua epämääräisten tunkeutujien tehtävää.

Tässä esimerkissä ei nyt tietenkään edes vihjattu, että näitä virheilmoituksia näytettäisiin käyttäjälle. Aloitteleva koodari voi kuitenkin tuosta viimeisestä koodipätkästä saada sellaisen kuvan.

Kiitos Metabolixille ansiokkaasta koodivinkistä :).

Metabolix [22.12.2012 14:11:17]

#

Arttut02 kirjoitti:

Käyttäjälle itselleen ei kannata ilmoittaa erikseen, että salasana on väärä. Tämä yleensä paljastaa sen, että käyttäjätunnus on kuitenkin ollut oikea.

Toisaalta monilla sivustoilla (kuten täälläkin) käyttäjätunnuksia saa helposti selville keskustelusta tai käyttäjien profiileja selaamalla, jolloin ei ole paljon hyötyä piilotella niitä muuallakaan. Myös käyttäjän kannalta on mukavampi saada selvä ilmoitus, ja käytännön haitta on olematon.

Arttut02 kirjoitti:

Sen jälkeen voidaan kyseisen käyttäjätunnuksen salasanaa ruveta kaivamaan esille omilla konsteillaan.

Mitähän ne konstit ovat? Netin yli kokeileminen on hidasta, tässä esitellyillä funktioilla siitä on tehty tarkoituksella vielä hitaampaa, ja joka tapauksessa on järkevää blokata väliaikaisesti osoitteet, joista tulee brute force -hyökkäyksiä.

Arttut02 [23.12.2012 00:27:48]

#

Metabolix kirjoitti:

Arttut02 kirjoitti:

Käyttäjälle itselleen ei kannata ilmoittaa erikseen, että salasana on väärä. Tämä yleensä paljastaa sen, että käyttäjätunnus on kuitenkin ollut oikea.

Toisaalta monilla sivustoilla (kuten täälläkin) käyttäjätunnuksia saa helposti selville keskustelusta tai käyttäjien profiileja selaamalla, jolloin ei ole paljon hyötyä piilotella niitä muuallakaan. Myös käyttäjän kannalta on mukavampi saada selvä ilmoitus, ja käytännön haitta on olematon.

Löytyy kuitenkin myös sivuja, missä ei käyttäjätunnuksia "jaeta" kaikkien nähtäville, esim. joku nettisivujen hallintajärjestelmä.

Hyökkääjä löytää tiensä tuolle hallintajärjestelmän sivulle. Hän käyttää jotain automaattista ohjelmaa, mikä ajaa läpi pitkän listan yleisistä käyttäjätunnuksista. Hyökkääjän ohjelma tunnistaa "väärä salasana"-ilmoituksen ja tekee listan kaikista oikeista käyttäjätunnuksista. Tämän jälkeen pystytään ajamaan läpi näille käyttäjätunnuksille lista tunnetuista salasanoista. Tämä nopeuttaa ja vähentää hyökkääjän työtä huomattavasti, kun ei tarvitse kokeilla jokaiselle yleiselle käyttäjätunnukselle jokaista yleistä salasanaa.

Tietysti käyttäjätunnus on harvoin mikään salaisuus (sehän näkyy yleensä kirjautuessa normaali tekstinä), mutta jos hyökkääjällä on halua, taitoa ja aikaa, niin miksi antaa ylimääräistä lisäetua, kun sen saa helposti estettyä.

Metabolix kirjoitti:

Arttut02 kirjoitti:

Sen jälkeen voidaan kyseisen käyttäjätunnuksen salasanaa ruveta kaivamaan esille omilla konsteillaan.

Mitähän ne konstit ovat? Netin yli kokeileminen on hidasta, tässä esitellyillä funktioilla siitä on tehty tarkoituksella vielä hitaampaa, ja joka tapauksessa on järkevää blokata väliaikaisesti osoitteet, joista tulee brute force -hyökkäyksiä.

Brute force, mahdolliset hyökkäykset sessioniin, social engineering. Voihan noita varmoja käyttäjätunnuksia jakaa muille kiinnostuneille ja varmasti monia muitakin tapoja löytyy. Kyllä näillä henkilöillä yleensä mielikuvitusta riittää. :)

Nämähän nyt ovat tietysti painotuskysymyksiä...

t0ll0 [26.12.2012 08:43:08]

#

Kiitos!

dartvaneri [22.03.2016 12:37:07]

#

Metabolix kirjoitti:

Mitähän ne konstit ovat? Netin yli kokeileminen on hidasta, tässä esitellyillä funktioilla siitä on tehty tarkoituksella vielä hitaampaa, ja joka tapauksessa on järkevää blokata väliaikaisesti osoitteet, joista tulee brute force -hyökkäyksiä.

Kyllä, brute force on hidasta netin yli, mikäli sitä ei tehdä hajautetusti, jolloin myöskään osoitteiden estämistä ei järkevästi voi toteuttaa.

Mutta itse opas vaikuttaa hyödylliseltä ja hyvältä. Onko tämä PHP:n oma tiivistealgoritmi tehokkaampi ja parempi kuin esimerkiksi SHA-256 tai BCrypt?

groovyb [15.02.2017 21:23:45]

#

brute forcea toki voi hankaloittaa bannaamalla vaikka käyttäjätili määräaikaisesti, kasvattaen bannin pituutta aina kun käyttäjätunnuksen salasana laitetaan väärin (vaikka aloittaen ekan viiden väärän salasanan jälkeen). vähän samaan tyyliin kuin puhelimen suojakoodi toimii.

Grez [16.02.2017 00:04:40]

#

dartvaneri kirjoitti:

Mutta itse opas vaikuttaa hyödylliseltä ja hyvältä. Onko tämä PHP:n oma tiivistealgoritmi tehokkaampi ja parempi kuin esimerkiksi SHA-256 tai BCrypt?

Riippuu käytetystä algoritmista. PHP 5.5 alkaen oletusalgoritmi on BCrypt, joten näillä esimerkkikoodeilla se on yhtä hyvä kuin BCrypt ja parempi kuin SHA-256. Toki BCryptin vaikeustasoa voi säätää. Se on suunniteltu sellaiseksi, että sitä voidaan vaikeuttaa sitä mukaa kun laskentateho kasvaa.

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta