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 →

Sigurni slučajni brojevi i tokeni u PHP-u pomoću random_bytes

Kako razvojni inženjeri u PHP-u stvaraju tokene za poveznice za lozinke, API ključeve i jednokratne kodove: kriptografski sigurni izvori, dovoljna duljina, spremanje kao hash i ograničena valjanost.

Stanje 5 min čitanjaSiguran razvoj

Ukratko: Sigurne tokene u PHP-u stvarate pomoću random_bytes(), primjerice 32 bajta koja pretvarate u tekst pomoću bin2hex() ili Base64-URL. Za slučajne brojeve u rasponu postoji random_int(), a za kodove iz fiksnog skupa znakova od PHP-a 8.3 Random\Randomizer::getBytesFromString(). Tokene spremate kao hash, ograničavate im valjanost, koristite ih samo jednom i uspoređujete pomoću hash_equals().

Poveznica koju je prelako pogoditi

„Zaboravljena lozinka?“ Jedan klik i sustav šalje poveznicu s dugim kodom. Tko poznaje poveznicu, može postaviti lozinku. Upravo zato taj kod mora biti nepredvidiv. Ako nije, napadaču ne trebaju ni poštanski sandučić ni stara lozinka, samo dovoljno pokušaja.

U provjerama PHP aplikacija začuđujuće se često nalaze tokeni sastavljeni od sata, ID-a korisnika ili jednostavnog generatora slučajnih brojeva. Izgledaju dugo i slučajno, ali nisu. Isto vrijedi za API ključeve, pozivne poveznice, poveznice za dijeljenje dokumenata, kodove za potvrdu i CSRF tokene.

Rizik je stvaran: predvidljiva reset poveznica omogućuje preuzimanje svakog računa čija je adresa e-pošte poznata, a da vlasnik ništa ne primijeti dok se i sam ne može prijaviti.

PHP od verzije 7.0 nudi jednostavno i sigurno rješenje. Samo ga treba dosljedno koristiti.

random_bytes u PHP-u: odakle dolaze sigurne slučajne vrijednosti

random_bytes() prema PHP priručniku daje kriptografski sigurne slučajne bajtove koji su prikladni i za dugovječne tajne poput ključeva. Funkcija koristi izvor slučajnosti operacijskog sustava, pod Linuxom primjerice getrandom(), pod Windowsom CNG sučelje. Ako ne nađe prikladan izvor, od PHP-a 8.2 baca Random\RandomException.

Pregled pokazuje koje su funkcije prikladne za tajne:

Funkcija Prikladna za tokene? Napomena
random_bytes() da sigurni bajtovi, od PHP-a 7.0
random_int() da siguran broj u rasponu, od PHP-a 7.0
Random\Randomizer sa Secure engineom da Secure je prema priručniku zadani engine
rand(), mt_rand() ne predvidljivi, samo za svrhe koje nisu sigurnosno relevantne
uniqid() ne temelji se na vremenu, namijenjen jedinstvenosti
md5(time()), sha1($id) ne ulaz se može pogoditi, hash to ne mijenja
str_shuffle(), array_rand() ne ne koriste siguran generator

Stvaranje tokena: duljina i kodiranje

32 slučajna bajta odgovaraju 256 bita i za gotovo svaku svrhu više su nego dovoljna. Za prijenos u poveznicama ili zaglavljima token se mora pretvoriti u tekst. Uobičajene su dvije varijante:

<?php
declare(strict_types=1);

// Hex: 64 znaka, samo 0-9 i a-f
$tokenHex = bin2hex(random_bytes(32));

// Base64-URL: 43 znaka, sigurno u URL-ovima bez dodatnog kodiranja
$tokenUrl = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');

// Slučajni broj u rasponu, primjerice za odabir
$index = random_int(0, 9);

// Od PHP-a 8.3: šesteroznamenkasti kod, npr. za potvrdu putem SMS-a
$randomizer = new \Random\Randomizer();
$code = $randomizer->getBytesFromString('0123456789', 6);

Kratki brojčani kodovi opravdani su samo ako je broj pokušaja strogo ograničen, a kod istječe nakon nekoliko minuta. Za poveznice koje se šalju e-poštom uvijek uzmite dugi token.

Za API ključeve koje partneri ili vlastiti sustavi koriste trajno, dokazale su se dvije dopune. Kratki, ne tajni prefiks ispred slučajnog dijela, primjerice za okruženje ili svrhu, olakšava prepoznavanje u zapisnicima i pri nalazima u repozitorijima koda. A ključ se prikazuje samo jednom pri izradi; nakon toga sustav poznaje samo njegov hash. Ako se ključ izgubi, zamjenjuje se, a ne obnavlja.

Oprez kod vlastoručno izgrađenih tokena koji sadrže informacije, primjerice ID korisnika i vrijeme isteka u Base64. Kodiranje nije zaštita: svatko takve tokene može pročitati i promijeniti. Ako token treba nositi podatke, mora biti potpisan tajnim ključem, primjerice pomoću hash_hmac('sha256', ...), a potpis se provjerava pomoću hash_equals(). Za složenije slučajeve provjerene biblioteke bolje su od vlastitih konstrukcija.

Spremanje, provjera i istek tokena

Sigurno stvoren token tek je pola posla. Barem je jednako važno kako aplikacija s njim postupa. Dokazan postupak za reset i pozivne poveznice:

  1. Spremiti samo hash: u bazi podataka stoji hash('sha256', $token), a sam token ide korisniku samo poveznicom.
  2. Ograničiti valjanost: spremiti i vrijeme isteka te ga provjeriti pri iskorištavanju.
  3. Upotrijebiti jednom: nakon uspješne uporabe obrisati unos ili označiti kao iskorišten.
  4. Poništiti pri promjenama: nakon promjene lozinke obrisati sve otvorene reset tokene računa.
  5. Uspoređivati konstantno: gdje se token izravno uspoređuje sa spremljenom vrijednošću, koristiti hash_equals(), sa spremljenom vrijednošću kao prvim parametrom.
<?php
// Stvaranje
$token = bin2hex(random_bytes(32));
$eintrag = [
    'konto_id'   => $kontoId,
    'token_hash' => hash('sha256', $token),
    'gueltig_bis'=> (new DateTimeImmutable('+30 minutes'))->format('Y-m-d H:i:s'),
];
// $token slati samo u poveznici, $eintrag spremiti

// Iskorištavanje
$gesucht = hash('sha256', (string) ($_GET['token'] ?? ''));
// Unos tražiti pomoću Prepared Statementa preko token_hash,
// provjeriti istek, zatim unos odmah obrisati

Ograničite i broj reset zahtjeva po računu i razdoblju, kako se funkcija ne bi mogla zloupotrijebiti za masovno slanje e-pošte. Odgovor na reset zahtjev uz to ne bi smio otkrivati ima li navedena adresa uopće račun.

Brzi hash poput SHA-256 ovdje je dovoljan jer je sam token već dug i slučajan. Za lozinke koje biraju ljudi to ne vrijedi; tamo trebaju spori postupci, kako objašnjava članak Sigurno spremati lozinke u PHP-u.

Savjet

Tokeni ne pripadaju u zapisnike. Provjerite bilježi li Vaš web-poslužitelj pune URL-ove s query parametrima i šalju li izvješća o pogreškama podatke zahtjeva.

Nakon incidenta: obnoviti tokene i ključeve

Tko je mogao izvršavati kod na poslužitelju, u pravilu je imao pristup i spremljenim tokenima, API ključevima i podacima sesija. Ako nakon PHP2Shella ili drugog incidenta postoji sumnja na provalu, trebali biste obnoviti ne samo lozinke, nego i dugovječne tokene i ključeve: API pristupe, ključeve za potpis kolačića ili poveznica, pristupe uslugama plaćanja i dostave.

Sesije su poseban slučaj: PHP ID-ove sesija stvara sam. Kako osigurati njihove postavke i prekinuti sesije nakon incidenta, opisuje vodič Osigurati sesije u PHP-u. Za tokene obrazaca protiv krivotvorenih zahtjeva vidi CSRF zaštita u PHP aplikacijama. Daljnje ugrađene alate predstavlja Ugrađene sigurnosne funkcije PHP-a.

Kako PHP SOS konkretno pomaže?

  • U Vašoj aplikaciji pronalazimo sva mjesta na kojima se stvaraju tokeni, kodovi i ključevi te zamjenjujemo nesigurne izvore.
  • Provjeravamo spremanje, istek i jednokratnu uporabu reset, pozivnih i poveznica za dijeljenje.
  • Osiguravamo da tokeni ne završe u zapisnicima, izvješćima o pogreškama ili alatima za analizu.
  • Nakon incidenta s Vama planiramo uredan zamjenu tokena i ključeva.

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

Česta pitanja

Zašto mt_rand nije prikladan za tokene?

mt_rand i rand namijenjeni su statistici i igrama, a ne tajnama. Njihovi izlazi pod određenim se uvjetima mogu predvidjeti. Za sve što napadač ne smije pogoditi koristite random_bytes ili random_int.

Koliko dug treba biti token?

Za poveznice za resetiranje lozinki, pozivnice ili API ključeve 32 slučajna bajta solidan su izbor, kao heksadecimalni tekst 64 znaka. 16 bajtova, dakle 128 bita, uobičajena je donja granica.

Je li uniqid siguran token?

Ne. uniqid se temelji na trenutačnom vremenu i namijenjen je jedinstvenosti, a ne tajnosti. I kombinacije poput md5 od vremena ili ID-a korisnika mogu se predvidjeti.

Moram li reset tokene u bazi podataka šifrirati?

Umjesto tokena spremite njegov hash, primjerice pomoću SHA-256. Tko čita bazu podataka, tako ne može izgraditi valjane poveznice. Budući da je sam token već dug i slučajan, ne treba spor postupak kao kod lozinki.

Što se događa ako sustav ne pronađe izvor slučajnosti?

random_bytes i random_int od PHP-a 8.2 bacaju Random\RandomException. Uhvatite taj slučaj i prekinite postupak umjesto da prijeđete na slabiji izvor.

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ć