Ajankohtainen tilanne: PHP2Shell. Kaksi kriittistä PHP-haavoittuvuutta (CVSS 9.2) sallii haittakoodin suorittamisen ilman kirjautumista. Korjaukset 21.10.2026 alkaen.Mitä nyt on tehtävä →

Salasanojen turvallinen tallennus PHP:ssä: password_hashin oikea käyttö

Miten kehittäjät tallentavat salasanat PHP:ssä password_hashilla ja password_verifylla, valitsevat sopivan menetelmän ja korvaavat vanhat MD5- tai SHA-1-tiivisteet ilman, että kaikkien käyttäjien salasana nollataan.

Tilanne Lukuaika 4 minTurvallinen kehitys

Lyhyesti: Salasanat tallennatte PHP:ssä funktiolla password_hash() ja tarkistatte ne funktiolla password_verify(). Oletus PASSWORD_DEFAULT käyttää tällä hetkellä bcryptiä, PHP 8.4:stä alkaen kustannuskertoimella 12; missä PHP on käännetty Argon2:n kanssa, PASSWORD_ARGON2ID on hyvä vaihtoehto. Vanhat MD5- tai SHA-1-tiivisteet korvaatte kääräisemällä ne heti password_hashiin ja korvaamalla ne seuraavassa kirjautumisessa suoralla tiivisteellä.

Mitä tietokantavuodolla on tekemistä tiivisteidenne kanssa

Verkkopalvelimeen tunkeutumisen jälkeen tietokanta on yksi ensimmäisistä kohteista. Joka voi PHP2Shellin tavoin suorittaa koodia palvelimella, lukee myös sovelluksen tunnukset ja sitä kautta käyttäjätilien taulun. Se, tulevatko siinä olevat salasanat luettaviksi tunneissa vai vuosissa, ei ratkea hyökkäyspäivänä vaan ohjelmoinnissa.

Yllättävän moni sovellus tallentaa salasanoja yhä menetelmillä, joita ei ole koskaan tarkoitettu tähän: MD5, SHA-1, yksinkertainen SHA-256 tai jopa palautettavasti salattuna. Nämä menetelmät ovat nopeita, ja juuri se on ongelma. Hyökkääjät voivat kokeilla niillä sekunnissa hyvin monta ehdokasta.

PHP:ssä on jo pitkään ollut ratkaisu, joka laskee hitaasti, luo jokaiselle salasanalle automaattisesti suolan (salt) ja tallentaa menetelmän ja parametrit itse tiivisteeseen. Teidän tarvitsee vain käyttää sitä johdonmukaisesti.

password_hash PHP:ssä: bcrypt vai Argon2id?

password_hash() tuntee useita menetelmiä. Valikoima PHP-käsikirjan mukaan:

Vakio Menetelmä Huomautus
PASSWORD_DEFAULT tällä hetkellä bcrypt oletus, voi muuttua tulevissa versioissa
PASSWORD_BCRYPT bcrypt kustannuskerroin PHP 8.4:stä alkaen oletuksena 12, aiemmin 10; vain 72 ensimmäistä tavua lasketaan
PASSWORD_ARGON2I Argon2i PHP 7.2:sta alkaen, vain jos PHP on rakennettu Argon2-tuella
PASSWORD_ARGON2ID Argon2id PHP 7.3:sta alkaen, sama edellytys

OWASP suosittelee ensisijaisesti Argon2id:tä vähintään 19 MiB muistilla, kahdella läpikäynnillä ja yhdellä säikeellä sekä bcryptiä lähinnä olemassa oleville järjestelmille. Useimmissa PHP-sovelluksissa ero on käytännössä pienempi kuin ero minkä tahansa näistä menetelmistä ja MD5:n välillä. Tärkeämpää on, että ylipäätään käytätte jotakin niistä ja mukautatte parametrit säännöllisesti.

Onko Argon2id käytettävissä, näyttää lyhyt testi:

<?php
var_dump(defined('PASSWORD_ARGON2ID'));
var_dump(password_algos());

Tallennus ja tarkistus: täydellinen malli

Seuraava esimerkki näyttää rekisteröinnin ja kirjautumisen sekä vanhentuneiden tiivisteiden automaattisen päivityksen. Pituustarkistus suojaa bcryptin 72 tavun rajalta:

<?php
declare(strict_types=1);

const PASSWORT_ALGO = PASSWORD_DEFAULT;

function passwortSpeichern(string $passwort): string
{
    if (strlen($passwort) > 72) {
        throw new InvalidArgumentException('Passwort zu lang für das gewählte Verfahren.');
    }
    return password_hash($passwort, PASSWORT_ALGO);
}

function passwortPruefen(string $eingabe, string $gespeicherterHash, callable $hashAktualisieren): bool
{
    if (!password_verify($eingabe, $gespeicherterHash)) {
        return false;
    }
    if (password_needs_rehash($gespeicherterHash, PASSWORT_ALGO)) {
        $hashAktualisieren(password_hash($eingabe, PASSWORT_ALGO));
    }
    return true;
}

strlen() laskee tavuja, ei merkkejä. Se on tässä tarkoituksellista, sillä bcryptin raja koskee tavuja. Tietokannan sarakkeen tulisi käsikirjan mukaan mahtua yli 60 merkkiä, 255 on hyvä valinta. Älkää koskaan asettako omaa suolaa: asetus on vanhentunut, ja se ohitetaan PHP 8.0:sta alkaen.

PHP 8.4:ään siirryttäessä bcryptin oletuskustannuskerroin nousee 10:stä 12:een. Olemassa olevat tiivisteet nostetaan funktion password_needs_rehash() ansiosta automaattisesti seuraavassa kirjautumisessa. Voimakkaasti kuormitetuilla järjestelmillä kannattaa ennen siirtymää tehdä lyhyt kuormitustesti kirjautumiselle.

Vinkki

Mitatkaa kustannuskerroin tuotantopalvelimellanne. Yhden tarkistuksen tulisi vaatia tuntuvasti laskenta-aikaa mutta pysyä selvästi alle sekunnissa, jottei kirjautumisista tule pullonkaulaa kuormitettuna.

Vanhojen MD5- ja SHA-1-tiivisteiden korvaaminen

Varsinainen haaste on yleensä vanha aineisto. Salasanoja itseään ette tunne, joten niitä ei voi vain tiivistää uudelleen. Jos odotatte, että jokainen käyttäjä kirjautuu uudelleen, passiiviset tilit jäävät suojaamatta. OWASP kuvaa siksi kaksivaiheisen toimintatavan:

  1. Kääriminen heti: Käärikää jokainen olemassa oleva MD5- tai SHA-1-arvo skriptillä funktiolla password_hash() ja tallentakaa merkinnän kanssa, esimerkiksi sarakkeeseen hash_typ.
  2. Tarkistus kirjautumisessa: Merkityillä tileillä laskekaa syöte ensin vanhalla menetelmällä ja tarkistakaa se sitten funktiolla password_verify() kääritystä arvoa vastaan.
  3. Suora korvaus: Luokaa onnistuneen kirjautumisen jälkeen tavallinen tiiviste selkotekstistä ja poistakaa merkintä.
  4. Jäännöskannan siivous: Asettakaa tilit, joilla on määräajan jälkeen yhä käärittyjä tiivisteitä, salasanan nollaukseen.
<?php
// Vaihe 1 (kerran skriptillä): $neu = password_hash($alterMd5Wert, PASSWORD_DEFAULT);
// Vaiheet 2 ja 3 kirjautumisessa:
if ($konto['hash_typ'] === 'md5-umhuellt') {
    $ok = password_verify(md5($eingabe), $konto['passwort_hash']);
    if ($ok) {
        $speichere(password_hash($eingabe, PASSWORD_DEFAULT), 'direkt');
    }
}

Käärityt tiivisteet ovat parempia kuin paljaat MD5-arvot mutta heikompia kuin suorat tiivisteet. Ne ovat siirtymäratkaisu, eivät tavoite. Tapahtuman jälkeen, jossa tietokanta voi olla kohteena, tämä muutos ei korvaa pyyntöä käyttäjille vaihtaa salasanansa. Mitkä jäljet viittaavat tällaiseen tapahtumaan, kuvaa Onko minulta jo murtauduttu?.

Pepper, salaus ja muut harhapolut

Jotkin ideat kuulostavat turvallisemmilta, mutta tuovat vähän hyötyä tai haittaavat:

  • Omat tiivistysrakenteet, kuten moninkertainen SHA-256 itse luodulla suolalla, ovat bcryptiä nopeampia ja siksi heikompia.
  • Salatut salasanat ovat palautettavissa. Jos avain on samalla palvelimella, salaus suojaa tunkeutumisen jälkeen tuskin lainkaan.
  • Pepper, eli kaikille salasanoille yhteinen salainen lisäarvo, voi olla lisäkerros, jos sitä säilytetään erillään tietokannasta. Se ei kuitenkaan korvaa hyvää tiivistysmenetelmää ja vaikeuttaa myöhempiä vaihtoja.
  • Ennakkotiivistys ennen bcryptiä 72 tavun rajan kiertämiseksi sisältää OWASPin mukaan omia riskejään. Selkeä pituusraja tai Argon2id on yksinkertaisempi.

Myös uusien salasanojen sääntöjen kohdalla kannattaa katsoa tekniikan nykytilaa: pituus suojaa enemmän kuin monimutkaiset erikoismerkkivaatimukset, ja hyvin yleiset tai tietovuodoissa jo esiintyneet salasanat tulisi hylätä. Pakotetut vaihdot kiinteinä väliajoin ilman syytä johtavat kokemuksen mukaan pikemminkin ennustettaviin kuvioihin kuin vahvempiin salasanoihin.

Kirjautumisjärjestelmissä salasanat eivät myöskään saa olla yksin: ylläpitäjien kaksivaiheinen tunnistautuminen ja massiivisten kirjautumisyritysten jarru täydentävät tallennusta. Molempia käsittelee opas Ylläpitäjien kirjautumisten suojaus raa'alta voimalta. Nollauslinkkeihin ja kertakoodeihin pätevät toiset säännöt, katso Turvalliset satunnaisluvut ja tokenit PHP:ssä. Yleiskuvan muista toiminnoista antaa PHP:n sisäänrakennetut tietoturvatoiminnot.

Miten PHP SOS auttaa konkreettisesti?

  • Tarkistamme, miten sovelluksenne ja laajennuksenne tallentavat salasanoja, ja löydämme vanhentuneet menetelmät myös sivutoiminnoista.
  • Suunnittelemme vanhan aineiston siirron kääreineen, kirjautumismigraatioineen ja määräaikoineen jäännöstileille.
  • Sovitamme kustannuskertoimen tai Argon2-parametrit palvelimiinne.
  • Tapahtuman jälkeen tuemme päätöksessä salasanojen nollauksista ja niiden tekniseen toteutukseen.

Oman PHP-ohjelmistonne tarkistuksen tarjoamme tietoturva-auditointina ja tunkeutumistestinä.

Usein kysytyt kysymykset

Mitä algoritmia password_hash käyttää asetuksella PASSWORD_DEFAULT?

Tällä hetkellä bcryptiä. PHP-käsikirja huomauttaa, että oletus voi muuttua, kun vahvempia menetelmiä tulee lisää. Siksi tietokantasarakkeen tulisi mahtua yli 60 merkkiä; käsikirja mainitsee hyvänä valintana 255.

Pitäisikö salasanat salata tiivistämisen sijaan?

Ei. Salatut salasanat voi lukea takaisin avaimella, ja tämä avain on yleensä samalla palvelimella. Salasanan tiiviste on yksisuuntainen katu: sovellus voi vain tarkistaa, täsmääkö syöte.

Kuinka korkea bcryptin kustannuskerroin pitäisi olla?

PHP 8.4:stä alkaen oletus on 12, aiemmin 10. OWASP mainitsee alarajaksi 10 ja suosittelee menemään niin korkealle kuin palvelimenne kestävät. Mitatkaa omalla laitteistollanne, kuinka kauan yksi tarkistus kestää.

Mitä bcryptin 72 tavun raja tarkoittaa?

bcrypt huomioi vain salasanan 72 ensimmäistä tavua, kaikki sen jälkeen katkaistaan. Skandinaavisilla ja muilla erikoismerkeillä yksi merkki vie usein kaksi tavua. Rajatkaa siksi pituus nimenomaisesti tai käyttäkää Argon2id:tä, jos PHP:nne on käännetty sen kanssa.

Pitääkö käyttäjien vaihtaa salasanansa tunkeutumisen jälkeen?

Jos ei voida sulkea pois, että tietokanta on vuotanut, teidän pitäisi vaatia sitä. Hyvät tiivisteet hidastavat murtamista huomattavasti, mutta heikot salasanat pysyvät haavoittuvina. Onko lisäksi ilmoitusvelvollisuus, selvittäkää tietosuojavastaavanne kanssa.

Lähteet

Pikayhteys

Jokainen tunti on tärkeä. Kirjoittakaa meille nyt.

Kuvatkaa lyhyesti järjestelmä, PHP-versio ja havainnot. Otamme yhteyttä, selvitämme tilanteen ja työskentelemme etänä, kunnes tapaus on käsitelty loppuun ja dokumentoitu.

Pikaapu