Trenutno stanje: PHP2Shell. Dvije kritične PHP ranjivosti (CVSS 9.2) omogućuju zlonamjerni kod bez prijave. Zakrpe od 21. 10. 2026.Što sada učiniti →

Sigurno spremati lozinke u PHP-u: ispravna primjena password_hash

Kako razvojni inženjeri u PHP-u spremaju lozinke pomoću password_hash i password_verify, biraju odgovarajući postupak i zamjenjuju stare MD5 ili SHA-1 hashove bez resetiranja lozinke svim korisnicima.

Stanje 5 min čitanjaSiguran razvoj

Ukratko: Lozinke u PHP-u spremate pomoću password_hash() i provjeravate pomoću password_verify(). Zadana vrijednost PASSWORD_DEFAULT trenutačno koristi bcrypt, od PHP-a 8.4 s faktorom troška 12; gdje je PHP preveden s Argon2, PASSWORD_ARGON2ID dobra je alternativa. Stare MD5 ili SHA-1 hashove zamjenjujete tako da ih odmah umotate u password_hash, a pri sljedećoj prijavi zamijenite izravnim hashom.

Kakve veze curenje baze podataka ima s Vašim hashovima

Nakon provale u web-poslužitelj baza podataka jedna je od prvih meta. Tko može izvršavati kod na poslužitelju, kao što je kod PHP2Shella moguće, čita i pristupne podatke aplikacije, a time i tablicu s korisničkim računima. Hoće li lozinke u njoj postati čitljive nakon nekoliko sati ili nakon nekoliko godina, ne odlučuje se na dan napada, nego pri programiranju.

Začuđujuće mnogo aplikacija još sprema lozinke postupcima koji za tu svrhu nikad nisu bili namijenjeni: MD5, SHA-1, običnim SHA-256 ili čak reverzibilnim šifriranjem. Ti su postupci brzi, i upravo je to problem. Napadači njima mogu isprobati vrlo mnogo kandidata u sekundi.

PHP već dugo donosi rješenje koje računa sporo, za svaku lozinku automatski stvara salt, a postupak i parametre sprema u sam hash. Morate ga samo dosljedno koristiti.

password_hash u PHP-u: bcrypt ili Argon2id?

password_hash() poznaje više postupaka. Izbor prema PHP priručniku:

Konstanta Postupak Napomena
PASSWORD_DEFAULT trenutačno bcrypt zadana vrijednost, može se promijeniti u budućim verzijama
PASSWORD_BCRYPT bcrypt faktor troška od PHP-a 8.4 zadano 12, prije 10; računa se samo prvih 72 bajta
PASSWORD_ARGON2I Argon2i od PHP-a 7.2, samo ako je PHP izgrađen s podrškom za Argon2
PASSWORD_ARGON2ID Argon2id od PHP-a 7.3, isti preduvjet

OWASP preporučuje Argon2id kao prvi izbor, s najmanje 19 MiB memorije, dva prolaza i jednom dretvom, a bcrypt ponajprije za postojeće sustave. Za većinu PHP aplikacija razlika je u praksi manja od razlike između bilo kojeg od tih postupaka i MD5-a. Važnije je da uopće koristite jedan od njih i redovito prilagođavate parametre.

Je li Argon2id dostupan, pokazuje kratki test:

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

Spremanje i provjera: potpun uzorak

Sljedeći primjer pokazuje registraciju i prijavu s automatskim osvježavanjem zastarjelih hashova. Provjera duljine štiti od ograničenja bcrypta od 72 bajta:

<?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() broji bajtove, a ne znakove. To je ovdje željeno, jer se ograničenje bcrypta odnosi na bajtove. Stupac u bazi podataka prema priručniku treba primiti više od 60 znakova, a 255 je dobar izbor. Nikad ne postavljajte vlastiti salt: opcija je zastarjela i od PHP-a 8.0 se ignorira.

Prelaskom na PHP 8.4 zadani faktor troška za bcrypt raste s 10 na 12. Postojeći hashovi zahvaljujući password_needs_rehash() pri sljedećoj prijavi automatski se podižu. Na jako opterećenim sustavima prije prelaska vrijedi kratki test opterećenja prijave.

Savjet

Faktor troška izmjerite na svojem produkcijskom poslužitelju. Jedna provjera trebala bi osjetno trošiti procesorsko vrijeme, ali ostati jasno ispod jedne sekunde, kako prijave pod opterećenjem ne bi postale usko grlo.

Zamijeniti stare MD5 i SHA-1 hashove

Pravi izazov obično je postojeći skup. Same lozinke ne znate, pa ih ne možete jednostavno ponovno hashirati. Čekanje da se svaki korisnik ponovno prijavi ostavlja neaktivne račune nezaštićenima. OWASP zato opisuje postupak u dvije faze:

  1. Odmah umotati: svaku postojeću MD5 ili SHA-1 vrijednost skriptom omotati pomoću password_hash() i spremiti s oznakom, primjerice u stupcu hash_typ.
  2. Pri prijavi provjeriti: za označene račune unos najprije preračunati starim postupkom, a zatim pomoću password_verify() provjeriti prema omotanoj vrijednosti.
  3. Izravno zamijeniti: nakon uspješne prijave iz otvorenog teksta stvoriti normalan hash i ukloniti oznaku.
  4. Očistiti ostatak: račune koji nakon određenog roka još imaju omotane hashove postaviti na reset lozinke.
<?php
// Faza 1 (jednokratno skriptom): $neu = password_hash($alterMd5Wert, PASSWORD_DEFAULT);
// Faze 2 i 3 pri prijavi:
if ($konto['hash_typ'] === 'md5-umhuellt') {
    $ok = password_verify(md5($eingabe), $konto['passwort_hash']);
    if ($ok) {
        $speichere(password_hash($eingabe, PASSWORD_DEFAULT), 'direkt');
    }
}

Omotani hashovi bolji su od golih MD5 vrijednosti, ali slabiji od izravnih hashova. Oni su prijelazno rješenje, a ne cilj. Nakon incidenta u kojem bi baza podataka mogla biti pogođena, ta preinaka ne zamjenjuje poziv korisnicima da promijene svoje lozinke. Koji tragovi upućuju na takav incident, opisuje članak Jesam li već kompromitiran?.

Pepper, šifriranje i drugi stranputci

Neke ideje zvuče kao više sigurnosti, ali donose malo ili štete:

  • Vlastite konstrukcije hasha, poput višestrukog SHA-256 s vlastitim saltom, brže su od bcrypta i zato slabije.
  • Šifrirane lozinke su reverzibilne. Ako ključ leži na istom poslužitelju, šifriranje nakon provale jedva štiti.
  • Pepper, dakle tajna dodatna vrijednost za sve lozinke, može biti dodatni sloj ako se čuva odvojeno od baze podataka. No ne zamjenjuje dobar postupak hashiranja i otežava kasnije promjene.
  • Prethodno hashiranje prije bcrypta radi zaobilaženja ograničenja od 72 bajta prema OWASP-u nosi vlastite rizike. Jasno ograničenje duljine ili Argon2id jednostavniji su.

I kod pravila za nove lozinke vrijedi pogledati stanje tehnike: duljina štiti više od složenih zahtjeva za posebne znakove, a vrlo česte lozinke ili one koje su se već pojavile u curenjima podataka treba odbiti. Prisilne promjene u fiksnim razmacima bez povoda prema iskustvu prije vode do predvidljivih obrazaca nego do jačih lozinki.

Za sustave prijave lozinke uz to nikad nisu same: dvofaktorski postupci za administratore i kočnica protiv masovnih pokušaja prijave nadopunjuju spremanje. Oboje obrađuje vodič Zaštititi administratorske prijave od brute forcea. Za linkove za reset i jednokratne kodove vrijede druga pravila, vidi Sigurni slučajni brojevi i tokeni u PHP-u. Pregled daljnjih funkcija daje članak Ugrađene sigurnosne funkcije PHP-a.

Kako PHP SOS konkretno pomaže?

  • Provjeravamo kako Vaša aplikacija i Vaši dodaci spremaju lozinke i nalazimo zastarjele postupke i u sporednim funkcijama.
  • Planiramo prelazak postojećeg skupa s omatanjem, migracijom pri prijavi i rokom za preostale račune.
  • Faktor troška ili Argon2 parametre usklađujemo s Vašim poslužiteljima.
  • Nakon incidenta pomažemo pri odluci o resetiranju lozinki i njihovoj tehničkoj provedbi.

Provjeru vlastitog PHP softvera nudimo kao sigurnosni audit i penetracijski test.

Česta pitanja

Koji algoritam koristi password_hash s PASSWORD_DEFAULT?

Trenutačno bcrypt. PHP priručnik upozorava da se zadana vrijednost može promijeniti kad se pojave jači postupci. Zato stupac u bazi podataka treba primiti više od 60 znakova; priručnik navodi 255 kao dobar izbor.

Trebam li lozinke šifrirati umjesto hashirati?

Ne. Šifrirane lozinke ponovno se mogu pročitati ključem, a taj ključ obično leži na istom poslužitelju. Hash lozinke je jednosmjerna ulica: aplikacija može samo provjeriti odgovara li unos.

Koliko visok treba biti faktor troška kod bcrypta?

Od PHP-a 8.4 zadana vrijednost je 12, prije toga 10. OWASP navodi 10 kao donju granicu i preporučuje ići onoliko visoko koliko Vaši poslužitelji podnose. Na vlastitom hardveru izmjerite koliko traje jedna provjera.

Što znači ograničenje od 72 bajta kod bcrypta?

bcrypt uzima u obzir samo prvih 72 bajta lozinke, sve nakon toga se odsijeca. Kod dijakritičkih znakova jedan znak često zauzima dva bajta. Zato izričito ograničite duljinu ili koristite Argon2id ako je Vaš PHP preveden s njime.

Moraju li korisnici nakon provale promijeniti lozinke?

Ako se ne može isključiti da je baza podataka iscurila, to biste trebali pokrenuti. Dobri hashovi znatno usporavaju probijanje, ali slabe lozinke ostaju ranjive. Postoji li uz to obveza prijave, razjasnite sa svojim službenikom za zaštitu podataka.

Izvori

Hitni kontakt

Svaki sat se računa. Pišite nam odmah.

Ukratko opišite sustav, verziju PHP-a i opažanje. Javit ćemo Vam se, razjasniti situaciju i raditi na daljinu dok incident nije zaključen i dokumentiran.

Hitna pomoć