Trenutno stanje: PHP2Shell. Dve kritični ranljivosti PHP (CVSS 9,2) omogočata izvedbo škodljive kode brez prijave. Popravki od 21. 10. 2026.Kaj je zdaj treba storiti →

Varno shranjevanje gesel v PHP: pravilna uporaba password_hash

Kako razvijalci v PHP shranjujejo gesla s password_hash in password_verify, izberejo ustrezen postopek ter nadomestijo stare zgoščene vrednosti MD5 ali SHA-1 brez ponastavitve gesel za vse uporabnike.

Stanje 5 min branjaVaren razvoj

Na kratko: Gesla v PHP shranjujete s password_hash() in preverjate s password_verify(). Privzeta vrednost PASSWORD_DEFAULT trenutno uporablja bcrypt, od PHP 8.4 s faktorjem stroška 12; kjer je PHP preveden z Argon2, je PASSWORD_ARGON2ID dobra alternativa. Stare zgoščene vrednosti MD5 ali SHA-1 preusmerite tako, da jih takoj ovijete v password_hash in ob naslednji prijavi nadomestite z neposredno zgoščeno vrednostjo.

Kaj ima uhajanje baze podatkov skupnega z vašimi zgoščenimi vrednostmi

Po vdoru v spletni strežnik je baza podatkov eden prvih ciljev. Kdor lahko, kot je pri PHP2Shell mogoče, na strežniku izvaja kodo, prebere tudi dostopne podatke aplikacije in s tem tabelo z uporabniškimi računi. Ali bodo gesla v njej postala berljiva čez ure ali čez leta, se ne odloči na dan napada, ampak pri programiranju.

Presenetljivo veliko aplikacij gesla še vedno shranjuje s postopki, ki za ta namen nikoli niso bili mišljeni: MD5, SHA-1, preprost SHA-256 ali celo povratno šifrirano. Ti postopki so hitri, in prav to je težava. Napadalci z njimi na sekundo preizkusijo zelo veliko kandidatov.

PHP že dolgo prinaša rešitev, ki računa počasi, za vsako geslo samodejno ustvari sol (salt) ter postopek in parametre shrani v samo zgoščeno vrednost. Uporabiti jo morate le dosledno.

password_hash v PHP: bcrypt ali Argon2id?

password_hash() pozna več postopkov. Izbira po priročniku PHP:

Konstanta Postopek Opomba
PASSWORD_DEFAULT trenutno bcrypt privzeto, lahko se spremeni v prihodnjih različicah
PASSWORD_BCRYPT bcrypt faktor stroška je od PHP 8.4 privzeto 12, prej 10; štejejo samo prvi 72 bajti
PASSWORD_ARGON2I Argon2i od PHP 7.2, samo če je PHP zgrajen s podporo za Argon2
PASSWORD_ARGON2ID Argon2id od PHP 7.3, enak pogoj

OWASP kot prvo izbiro priporoča Argon2id, z vsaj 19 MiB pomnilnika, dvema prehodoma in eno nitjo, bcrypt pa predvsem za obstoječe sisteme. Za večino aplikacij PHP je razlika v praksi manjša od razlike med katerimkoli od teh postopkov in MD5. Pomembnejše je, da sploh uporabljate enega od njih in parametre redno prilagajate.

Ali je Argon2id na voljo, pokaže kratek preizkus:

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

Shranjevanje in preverjanje: popoln vzorec

Naslednji primer prikazuje registracijo in prijavo s samodejno posodobitvijo zastarelih zgoščenih vrednosti. Preverjanje dolžine ščiti pred omejitvijo 72 bajtov pri bcrypt:

<?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() šteje bajte, ne znakov. To je tu namerno, saj se omejitev bcrypt nanaša na bajte. Stolpec v bazi podatkov naj po priročniku sprejme več kot 60 znakov, 255 je dobra izbira. Nikoli ne nastavljajte lastne soli: ta možnost je zastarela in se od PHP 8.0 prezre.

S prehodom na PHP 8.4 se privzeti faktor stroška za bcrypt dvigne z 10 na 12. Obstoječe zgoščene vrednosti se zahvaljujoč password_needs_rehash() ob naslednji prijavi samodejno dvignejo. Na močno obremenjenih sistemih se pred prehodom splača kratek obremenitveni preizkus prijave.

Nasvet

Faktor stroška izmerite na svojem produkcijskem strežniku. Eno preverjanje naj občutno porabi računski čas, a naj ostane precej pod eno sekundo, da prijave pri obremenitvi ne postanejo ozko grlo.

Zamenjava starih zgoščenih vrednosti MD5 in SHA-1

Pravi izziv je običajno obstoječi sklad. Samih gesel ne poznate, zato jih ne morete preprosto znova zgostiti. Čakanje, da se vsak uporabnik spet prijavi, pusti neaktivne račune nezaščitene. OWASP zato opisuje dvostopenjski postopek:

  1. Takoj ovijte: vsako obstoječo vrednost MD5 ali SHA-1 s skriptom ovijte s password_hash() in shranite z oznako, na primer v stolpcu hash_typ.
  2. Preverite ob prijavi: za označene račune vnos najprej preračunajte s starim postopkom in ga nato s password_verify() preverite z ovito vrednostjo.3. Takoj nadomestite: po uspešni prijavi iz golega besedila ustvarite običajno zgoščeno vrednost in odstranite oznako.
  3. Počistite ostanek: račune, ki imajo po določenem roku še vedno ovite zgoščene vrednosti, nastavite na ponastavitev gesla.
<?php
// Stopnja 1 (enkrat s skriptom): $neu = password_hash($alterMd5Wert, PASSWORD_DEFAULT);
// Stopnji 2 in 3 ob prijavi:
if ($konto['hash_typ'] === 'md5-umhuellt') {
    $ok = password_verify(md5($eingabe), $konto['passwort_hash']);
    if ($ok) {
        $speichere(password_hash($eingabe, PASSWORD_DEFAULT), 'direkt');
    }
}

Ovite zgoščene vrednosti so boljše od golih vrednosti MD5, a šibkejše od neposrednih zgoščenih vrednosti. So prehodna rešitev, ne cilj. Po incidentu, pri katerem bi bila lahko prizadeta baza podatkov, ta prehod ne nadomesti poziva uporabnikom, naj spremenijo gesla. Katere sledi kažejo na tak incident, opisuje Ali smo že ogroženi?.

Pepper, šifriranje in druge zablode

Nekatere zamisli zvenijo kot več varnosti, a prinesejo malo ali škodijo:

  • Lastne konstrukcije zgoščevanja, kot je večkratni SHA-256 z lastno ustvarjeno soljo, so hitrejše od bcrypt in zato šibkejše.
  • Šifrirana gesla so povratna. Če ključ leži na istem strežniku, šifriranje po vdoru skoraj ne ščiti.
  • Pepper, torej skrivna dodatna vrednost za vsa gesla, je lahko dodatna plast, če se hrani ločeno od baze podatkov. Dobrega postopka zgoščevanja pa ne nadomesti in oteži poznejše menjave.
  • Predhodno zgoščevanje pred bcrypt za obhod omejitve 72 bajtov po OWASP prinaša lastna tveganja. Jasna omejitev dolžine ali Argon2id je preprostejša.

Tudi pri pravilih za nova gesla se splača pogledati stanje tehnike: dolžina ščiti bolj kot zapletene zahteve glede posebnih znakov, zelo pogosta gesla ali gesla, ki so se že pojavila v uhajanjih podatkov, pa naj se zavrnejo. Prisilne menjave v stalnih presledkih brez povoda po izkušnjah prej vodijo do predvidljivih vzorcev kot do močnejših gesel.

Za prijavne sisteme gesla tudi nikoli niso sama: shranjevanje dopolnjujeta dvofaktorski postopek za skrbnike in zavora proti množičnim poskusom prijave. Obe obravnava vodnik Zaščita skrbniških prijav pred brute force. Za povezave za ponastavitev in enkratne kode veljajo druga pravila, glejte Varna naključna števila in žetoni v PHP. Pregled nadaljnjih funkcij daje Vgrajene varnostne funkcije PHP.

Kako konkretno pomaga PHP SOS?

  • Preverimo, kako vaša aplikacija in vtičniki shranjujejo gesla, ter najdemo zastarele postopke tudi v stranskih funkcijah.
  • Prehod obstoječega sklada načrtujemo z ovijanjem, migracijo ob prijavi in rokom za preostale račune.
  • Faktor stroška ali parametre Argon2 uskladimo z vašimi strežniki.
  • Po incidentu pomagamo pri odločitvi o ponastavitvah gesel in njihovi tehnični izvedbi.

Pregled lastne programske opreme PHP ponujamo kot varnostni pregled in penetracijski test.

Pogosta vprašanja

Kateri algoritem uporablja password_hash s PASSWORD_DEFAULT?

Trenutno bcrypt. Priročnik PHP opozarja, da se lahko privzeta vrednost spremeni, ko se pojavijo močnejši postopki. Zato naj stolpec v bazi podatkov sprejme več kot 60 znakov; priročnik kot dobro izbiro navaja 255.

Ali naj gesla šifriram namesto zgoščujem?

Ne. Šifrirana gesla je s ključem mogoče spet prebrati, ta ključ pa večinoma leži na istem strežniku. Zgoščena vrednost gesla je enosmerna ulica: aplikacija lahko samo preveri, ali vnos ustreza.

Kako visok naj bo faktor stroška pri bcrypt?

Od PHP 8.4 je privzeta vrednost 12, prej 10. OWASP navaja 10 kot spodnjo mejo in priporoča, da gredo tako visoko, kot zmorejo vaši strežniki. Na lastni strojni opremi izmerite, kako dolgo traja eno preverjanje.

Kaj pomeni omejitev 72 bajtov pri bcrypt?

bcrypt upošteva samo prvih 72 bajtov gesla, vse za tem se odreže. Pri šumnikih in preglasih en znak pogosto šteje dva bajta. Zato dolžino izrecno omejite ali uporabite Argon2id, če je vaš PHP preveden z njim.

Ali morajo uporabniki po vdoru spremeniti gesla?

Če ni mogoče izključiti, da je baza podatkov pricurljala ven, naj to sprožite. Dobre zgoščene vrednosti zlom bistveno upočasnijo, šibka gesla pa ostanejo ranljiva. Ali obstaja še obveznost prijave, razjasnite s pooblaščencem za varstvo podatkov.

Viri

Takojšnji stik

Vsaka ura šteje. Pišite nam zdaj.

Na kratko opišite sistem, različico PHP in opažanje. Odzovemo se, razjasnimo stanje in delamo na daljavo, dokler incident ni zaključen in dokumentiran.

Takojšnja pomoč