Aktuálna situácia: PHP2Shell. Dve kritické zraniteľnosti PHP (CVSS 9.2) umožňujú spustenie škodlivého kódu bez prihlásenia. Záplaty od 21. 10. 2026.Čo treba urobiť teraz →

Bezpečné náhodné čísla a tokeny v PHP pomocou random_bytes

Ako vývojári vytvárajú v PHP tokeny pre odkazy na heslo, kľúče API a jednorazové kódy: kryptograficky bezpečné zdroje, dostatočná dĺžka, ukladanie ako hash a obmedzená platnosť.

Stav 5 min čítaniaBezpečný vývoj

Stručne: Bezpečné tokeny vytvoríte v PHP pomocou random_bytes(), napríklad 32 bajtov, ktoré pomocou bin2hex() alebo Base64-URL prevediete na text. Pre náhodné čísla v rozsahu slúži random_int(), pre kódy z pevnej zásoby znakov od PHP 8.3 Random\Randomizer::getBytesFromString(). Tokeny ukladajte ako hash, obmedzte ich platnosť, používajte ich len raz a porovnávajte ich pomocou hash_equals().

Odkaz, ktorý sa dá príliš ľahko uhádnuť

„Zabudli ste heslo?“ Jedno kliknutie a systém odošle odkaz s dlhým kódom. Kto odkaz pozná, môže nastaviť heslo. Práve preto musí byť tento kód nepredvídateľný. Ak nie je, útočník nepotrebuje ani schránku, ani staré heslo, len dosť pokusov.

Pri kontrolách aplikácií PHP sa prekvapivo často nájdu tokeny zložené z času, ID používateľa alebo jednoduchého generátora náhodných čísel. Vyzerajú dlho a náhodne, no nie sú. To isté platí pre kľúče API, pozývacie odkazy, odkazy na zdieľanie dokumentov, potvrdzovacie kódy a tokeny CSRF.

Riziko je hmatateľné: predvídateľný odkaz na reset umožňuje prevziať každý účet, ktorého e-mailová adresa je známa, bez toho, aby si majiteľ niečo všimol, kým sa sám neprestane vedieť prihlásiť.

PHP ponúka od verzie 7.0 jednoduché a bezpečné riešenie. Stačí ho dôsledne používať.

random_bytes v PHP: odkiaľ pochádzajú bezpečné náhodné hodnoty

random_bytes() poskytuje podľa príručky PHP kryptograficky bezpečné náhodné bajty, ktoré sa hodia aj pre dlhodobé tajomstvá ako kľúče. Funkcia používa zdroj náhodnosti operačného systému, pod Linuxom napríklad getrandom(), pod Windows rozhranie CNG. Ak nenájde vhodný zdroj, vyhodí od PHP 8.2 výnimku Random\RandomException.

Prehľad ukazuje, ktoré funkcie sa pre tajomstvá hodia:

Funkcia Vhodná pre tokeny? Poznámka
random_bytes() áno bezpečné bajty, od PHP 7.0
random_int() áno bezpečné číslo v rozsahu, od PHP 7.0
Random\Randomizer so Secure Engine áno Secure je podľa príručky štandardný engine
rand(), mt_rand() nie predvídateľné, len na účely nedôležité pre bezpečnosť
uniqid() nie založené na čase, určené na jedinečnosť
md5(time()), sha1($id) nie vstup sa dá uhádnuť, hash na tom nič nemení
str_shuffle(), array_rand() nie nepoužívajú bezpečný generátor

Vytváranie tokenov: dĺžka a kódovanie

32 náhodných bajtov zodpovedá 256 bitom a pre takmer každý účel je to viac než dosť. Na prenos v odkazoch alebo hlavičkách sa musí token previesť na text. Obvyklé sú dva varianty:

<?php
declare(strict_types=1);

// Hex: 64 znakov, len 0-9 a a-f
$tokenHex = bin2hex(random_bytes(32));

// Base64-URL: 43 znakov, bezpečné v URL bez ďalšieho kódovania
$tokenUrl = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');

// Náhodné číslo v rozsahu, napríklad pre výber
$index = random_int(0, 9);

// Od PHP 8.3: šesťmiestny číselný kód, napr. na potvrdenie cez SMS
$randomizer = new \Random\Randomizer();
$code = $randomizer->getBytesFromString('0123456789', 6);

Krátke číselné kódy sú obhájiteľné len vtedy, keď je počet pokusov prísne obmedzený a kód po niekoľkých minútach zaniká. Pre odkazy posielané e-mailom vždy použite dlhý token.

Pre kľúče API, ktoré partneri alebo vlastné systémy používajú trvalo, sa osvedčili dva doplnky. Krátka, netajná predpona pred náhodnou časťou, napríklad pre prostredie alebo účel, uľahčuje rozpoznanie v protokoloch a pri nálezoch v repozitároch kódu. A kľúč sa zobrazí len raz pri vytvorení; potom systém pozná už len jeho hash. Ak sa kľúč stratí, nahradí sa, neobnovuje sa.

Pozor pri vlastnoručne zostavených tokenoch, ktoré obsahujú informácie, napríklad ID používateľa a čas platnosti v Base64. Kódovanie nie je zabezpečenie: každý môže takéto tokeny čítať a meniť. Ak má token niesť údaje, musí byť podpísaný tajným kľúčom, napríklad pomocou hash_hmac('sha256', ...), a podpis sa kontroluje pomocou hash_equals(). Pre náročnejšie prípady sú preverené knižnice vhodnejšie než vlastné konštrukcie.

Ukladanie, overovanie a zánik tokenov

Bezpečne vytvorený token je len polovica úspechu. Prinajmenšom rovnako dôležité je, ako s ním aplikácia zaobchádza. Osvedčený postup pre odkazy na reset a pozvánky:

  1. Ukladať len hash: v databáze je hash('sha256', $token), samotný token ide používateľovi iba cez odkaz.
  2. Obmedziť platnosť: uložiť čas zániku a skontrolovať ho pri uplatnení.
  3. Použiť raz: po úspešnom použití záznam zmazať alebo označiť ako spotrebovaný.
  4. Pri zmenách zneplatniť: po zmene hesla zmazať všetky otvorené tokeny na reset daného účtu.
  5. Porovnávať v konštantnom čase: kde sa token porovnáva priamo s uloženou hodnotou, použiť hash_equals() s uloženou hodnotou ako prvým parametrom.
<?php
// Vytvorenie
$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 poslať len v odkaze, $eintrag uložiť

// Uplatnenie
$gesucht = hash('sha256', (string) ($_GET['token'] ?? ''));
// záznam vyhľadať cez token_hash pomocou Prepared Statement,
// skontrolovať platnosť, potom záznam okamžite zmazať

Obmedzte navyše počet požiadaviek na reset na účet a časové obdobie, aby sa funkcia nedala zneužiť na hromadné odosielanie e-mailov. Odpoveď na požiadavku na reset by navyše nemala prezradiť, či zadaná adresa vôbec má účet.

Rýchly hash ako SHA-256 tu stačí, pretože samotný token je už dlhý a náhodný. Pre heslá, ktoré volia ľudia, to neplatí; tam sú potrebné pomalé postupy, ako vysvetľuje Bezpečné ukladanie hesiel v PHP.

Tip

Tokeny nepatria do súborov protokolov. Skontrolujte, či váš webserver nezaznamenáva úplné URL s parametrami query a či chybové správy neposielajú údaje požiadavky.

Po incidente: obnoviť tokeny a kľúče

Kto mohol spúšťať kód na serveri, mal spravidla prístup aj k uloženým tokenom, kľúčom API a údajom relácií. Ak po PHP2Shell alebo inom incidente vzniká podozrenie na vlámanie, mali by ste preto obnoviť nielen heslá, ale aj dlhodobé tokeny a kľúče: prístupy API, podpisové kľúče pre cookies alebo odkazy, prístupy k platobným a prepravným službám.

Relácie sú zvláštny prípad: ID relácií vytvára PHP samo. Ako zabezpečiť ich nastavenia a ukončiť relácie po incidente, opisuje sprievodca Zabezpečenie relácií v PHP. K tokenom vo formulároch proti sfalšovaným požiadavkám pozri Ochrana pred CSRF v aplikáciách PHP. Ďalšie zabudované nástroje predstavuje Zabudované bezpečnostné funkcie PHP.

Ako konkrétne pomáha PHP SOS?

  • Nájdeme vo vašej aplikácii všetky miesta, kde sa vytvárajú tokeny, kódy a kľúče, a nahradíme nebezpečné zdroje.
  • Preveríme ukladanie, zánik a jednorazové použitie odkazov na reset, pozvánok a odkazov na zdieľanie.
  • Postaráme sa o to, aby tokeny neskončili v protokoloch, chybových správach ani analytických nástrojoch.
  • Po incidente s vami naplánujeme usporiadanú výmenu tokenov a kľúčov.

Kontrolu vlastného softvéru PHP ponúkame ako bezpečnostný audit a penetračný test.

Časté otázky

Prečo je mt_rand nevhodný pre tokeny?

mt_rand a rand sú určené pre štatistiku a hry, nie pre tajomstvá. Ich výstupy sa za určitých okolností dajú predvídať. Pre všetko, čo útočník nesmie uhádnuť, použite random_bytes alebo random_int.

Aký dlhý by mal byť token?

Pre odkazy na obnovenie hesla, pozvánky alebo kľúče API sú solídnou voľbou 32 náhodných bajtov, ako text hex 64 znakov. 16 bajtov, teda 128 bitov, je rozšírená spodná hranica.

Je uniqid bezpečný token?

Nie. uniqid vychádza z aktuálneho času a je určený na jedinečnosť, nie na utajenie. Aj kombinácie ako md5 z času alebo ID používateľa sú predvídateľné.

Musím tokeny na reset v databáze šifrovať?

Namiesto tokenu ukladajte jeho hash, napríklad pomocou SHA-256. Kto číta databázu, nemôže z toho zostaviť platné odkazy. Keďže samotný token je už dlhý a náhodný, nepotrebuje pomalý postup ako pri heslách.

Čo sa stane, ak systém nenájde zdroj náhodnosti?

random_bytes a random_int vyhadzujú od PHP 8.2 výnimku Random\RandomException. Tento prípad zachyťte a proces prerušte, namiesto prechodu na slabší zdroj.

Zdroje

Okamžitý kontakt

Každá hodina sa počíta. Napíšte nám hneď.

Stručne opíšte systém, verziu PHP a svoje pozorovanie. Ozveme sa, vyjasníme situáciu a budeme pracovať na diaľku, kým nebude incident uzavretý a zdokumentovaný.

Okamžitá pomoc