Praegune olukord: PHP2Shell. Kaks kriitilist PHP turvaauku (CVSS 9,2) lubavad kahjulikku koodi ilma sisselogimata. Paigad alates 21.10.2026.Mida nüüd teha →

Turvalised juhuarvud ja tokenid PHP-s funktsiooniga random_bytes

Kuidas arendajad loovad PHP-s tokeneid parooli lähtestamise linkide, API-võtmete ja ühekordsete koodide jaoks: krüptograafiliselt turvalised allikad, piisav pikkus, salvestamine räsina ja piiratud kehtivus.

Seis Lugemisaeg 5 minTurvaline arendus

Lühidalt: Turvalisi tokeneid loote PHP-s funktsiooniga random_bytes(), umbes 32 baiti, mille teisendate tekstiks funktsiooniga bin2hex() või Base64-URL-iga. Juhuarvude jaoks vahemikus on random_int(), koodide jaoks kindlast märgivarast PHP 8.3-st Random\Randomizer::getBytesFromString(). Tokeneid salvestate räsina, piirate nende kehtivust, kasutate neid ainult ühe korra ja võrdlete funktsiooniga hash_equals().

„Parool ununenud?“ Üks klõps ja süsteem saadab lingi pika koodiga. Kes linki tunneb, saab parooli seada. Just seetõttu peab see kood olema ettearvamatu. Kui see seda ei ole, ei vaja ründaja ei postkasti ega vana parooli, ainult piisavalt katseid.

PHP-rakenduste kontrollides leidub üllatavalt sageli tokeneid, mis on ehitatud kellaajast, kasutaja ID-st või lihtsast juhuarvugeneraatorist. Need näevad välja pikad ja juhuslikud, kuid ei ole seda. Sama kehtib API-võtmete, kutselinkide, dokumentide jagamislinkide, kinnituskoodide ja CSRF-tokenite kohta.

Risk on käegakatsutav: ettearvatav lähtestuslink võimaldab üle võtta iga konto, mille e-posti aadress on teada, ilma et omanik midagi märkaks, kuni ta ise enam sisse logida ei saa.

PHP pakub alates versioonist 7.0 lihtsat ja turvalist lahendust. Seda tuleb ainult järjekindlalt kasutada.

random_bytes PHP-s: kust turvalised juhuväärtused tulevad

random_bytes() tagastab PHP käsiraamatu kohaselt krüptograafiliselt turvalisi juhuslikke baite, mis sobivad ka pikaealiste saladuste, nagu võtmete, jaoks. Funktsioon kasutab operatsioonisüsteemi juhuslikkuse allikat, Linuxis näiteks getrandom(), Windowsis CNG liidest. Kui sobivat allikat ei leita, viskab see PHP 8.2-st alates erandi Random\RandomException.

Ülevaade näitab, millised funktsioonid saladuste jaoks sobivad:

Funktsioon Kas sobib tokenite jaoks? Märkus
random_bytes() jah turvalised baidid, PHP 7.0-st
random_int() jah turvaline arv vahemikus, PHP 7.0-st
Random\Randomizer Secure-mootoriga jah Secure on käsiraamatu kohaselt vaikemootor
rand(), mt_rand() ei ettearvatavad, ainult mitteturvaküsimustega seotud eesmärkideks
uniqid() ei ajapõhine, mõeldud unikaalsuseks
md5(time()), sha1($id) ei sisend äraarvatav, räsi ei muuda seda
str_shuffle(), array_rand() ei ei kasuta turvalist generaatorit

Tokenite loomine: pikkus ja kodeering

32 juhuslikku baiti vastavad 256 bitile ja on peaaegu igaks otstarbeks enam kui piisavad. Linkides või päistes edastamiseks tuleb token tekstiks teisendada. Kaks varianti on tavalised:

<?php
declare(strict_types=1);

// Heks: 64 märki, ainult 0-9 ja a-f
$tokenHex = bin2hex(random_bytes(32));

// Base64-URL: 43 märki, turvaline URL-ides ilma täiendava kodeerimiseta
$tokenUrl = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');

// Juhuarv vahemikus, näiteks valiku jaoks
$index = random_int(0, 9);

// PHP 8.3-st: kuuekohaline numbrikood, nt SMS-iga kinnitamiseks
$randomizer = new \Random\Randomizer();
$code = $randomizer->getBytesFromString('0123456789', 6);

Lühikesed numbrikoodid on õigustatud ainult siis, kui katsete arv on rangelt piiratud ja kood aegub mõne minuti pärast. E-postiga saadetavate linkide jaoks võtke alati pikk token.

API-võtmete jaoks, mida partnerid või oma süsteemid püsivalt kasutavad, on end tõestanud kaks täiendust. Lühike, mittesalajane eesliide enne juhuslikku osa, näiteks keskkonna või eesmärgi jaoks, lihtsustab äratundmist logides ja leidude puhul koodirepositooriumides. Ja võtit näidatakse ainult üks kord loomisel; pärast seda tunneb süsteem enam ainult selle räsi. Kui võti läheb kaotsi, asendatakse see, mitte ei taastata.

Ettevaatust ise ehitatud tokenitega, mis sisaldavad teavet, näiteks kasutaja ID ja aegumisaeg Base64-s. Kodeerimine ei ole kaitse: igaüks saab selliseid tokeneid lugeda ja muuta. Kui token peab andmeid kandma, peab see olema allkirjastatud salajase võtmega, näiteks hash_hmac('sha256', ...) abil, ja allkirja kontrollitakse funktsiooniga hash_equals(). Keerukamate juhtude jaoks on kontrollitud teegid parem kui oma konstruktsioonid.

Tokenite salvestamine, kontrollimine ja aegumine

Turvaliselt loodud token on ainult pool võitu. Vähemalt sama oluline on, kuidas rakendus sellega ümber käib. Lähtestus- ja kutselinkide jaoks on end tõestanud see lähenemine:

  1. Salvestage ainult räsi: Andmebaasis on hash('sha256', $token), token ise läheb kasutajale ainult lingiga.
  2. Piirake kehtivust: Salvestage aegumisaeg kaasa ja kontrollige seda sisselunastamisel.
  3. Kasutage ühe korra: Pärast edukat kasutamist kustutage kirje või märkige see kasutatuks.
  4. Muudatuste korral tühistage: Pärast parooli muutmist kustutage konto kõik avatud lähtestustokenid.
  5. Võrrelge konstantse ajaga: Kus tokenit võrreldakse otse salvestatud väärtusega, kasutage hash_equals(), salvestatud väärtusega esimese parameetrina.
<?php
// Loomine
$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 saata ainult lingis, $eintrag salvestada

// Sisselunastamine
$gesucht = hash('sha256', (string) ($_GET['token'] ?? ''));
// Otsige kirje Prepared Statement'iga token_hash järgi,
// kontrollige aegumist, seejärel kustutage kirje kohe

Piirake lisaks lähtestuspäringute arvu konto ja ajavahemiku kohta, et funktsiooni ei saaks kuritarvitada e-kirjade massiliseks saatmiseks. Lähtestuspäringu vastus ei tohiks lisaks reeta, kas sisestatud aadressil üldse konto on.

Kiire räsi, nagu SHA-256, piisab siin, sest token ise on juba pikk ja juhuslik. Inimeste valitud paroolide puhul see ei kehti; seal on vaja aeglaseid meetodeid, nagu selgitab Paroolide turvaline salvestamine PHP-s.

Näpunäide

Tokenid ei kuulu logifailidesse. Kontrollige, kas teie veebiserver logib täielikke URL-e koos päringuparameetritega ja kas veateated saadavad päringuandmeid kaasa.

Pärast intsidenti: tokenite ja võtmete uuendamine

Kes sai serveris koodi käivitada, sai tavaliselt ligipääsu ka salvestatud tokenitele, API-võtmetele ja sessiooniandmetele. Kui PHP2Shelli või muu intsidendi järel on kahtlus sissemurdmises, peaksite seetõttu uuendama mitte ainult paroole, vaid ka pikaealisi tokeneid ja võtmeid: API-ligipääsud, küpsiste või linkide allkirjastamisvõtmed, ligipääsud makse- ja saatmisteenustele.

Sessioonid on erijuht: PHP loob sessiooni-ID-d ise. Kuidas nende sätteid kaitsta ja sessioone pärast intsidenti lõpetada, kirjeldab juhend Sessioonide kaitsmine PHP-s. Võltsitud päringute vastaste vormitokenite kohta vt CSRF-kaitse PHP-rakendustes. Muid sisseehitatud tööriistu tutvustab PHP sisseehitatud turvafunktsioonid.

Kuidas PHP SOS konkreetselt aitab?

  • Me leiame teie rakendusest kõik kohad, kus tokeneid, koode ja võtmeid luuakse, ning asendame ebaturvalised allikad.
  • Me kontrollime lähtestus-, kutse- ja jagamislinkide salvestamist, aegumist ja ühekordset kasutamist.
  • Me hoolitseme selle eest, et tokenid ei satuks logidesse, veeraportitesse ega analüüsitööriistadesse.
  • Pärast intsidenti planeerime teiega tokenite ja võtmete korrapärase vahetamise.

Oma PHP-tarkvara kontrolli pakume turvaauditi ja läbitungimistestina.

Korduma kippuvad küsimused

Miks ei sobi mt_rand tokenite jaoks?

mt_rand ja rand on mõeldud statistikaks ja mängudeks, mitte saladuste jaoks. Nende väljundeid saab teatud tingimustel ette ennustada. Kõige jaoks, mida ründaja ei tohi ära arvata, kasutage random_bytes või random_int.

Kui pikk peaks token olema?

Paroolide lähtestamise linkide, kutsete või API-võtmete jaoks on 32 juhuslikku baiti kindel valik, heksatekstina 64 märki. 16 baiti ehk 128 bitti on levinud alumine piir.

Kas uniqid on turvaline token?

Ei. uniqid põhineb praegusel ajal ja on mõeldud unikaalsuseks, mitte saladuse hoidmiseks. Ka kombinatsioonid, nagu aja või kasutaja ID md5, on ettearvatavad.

Kas pean lähtestustokeneid andmebaasis krüpteerima?

Salvestage tokeni asemel selle räsi, näiteks SHA-256-ga. Kes andmebaasi loeb, ei saa sellega kehtivaid linke ehitada. Kuna token ise on juba pikk ja juhuslik, ei ole vaja aeglast meetodit nagu paroolide puhul.

Mis juhtub, kui süsteem ei leia juhuslikkuse allikat?

random_bytes ja random_int viskavad PHP 8.2-st alates erandi Random\RandomException. Püüdke see juhtum kinni ja katkestage toiming, selle asemel et kasutada nõrgemat allikat.

Allikad

Kohene kontakt

Iga tund loeb. Kirjutage meile kohe.

Kirjeldage lühidalt süsteemi, PHP versiooni ja tähelepanekut. Võtame teiega ühendust, selgitame olukorra välja ja töötame kaugtööna, kuni intsident on lõpetatud ja dokumenteeritud.

Kohene abi