Aktuālā situācija: PHP2Shell. Divas kritiskas PHP ievainojamības (CVSS 9.2) ļauj izpildīt ļaunprātīgu kodu bez pieteikšanās. Ielāpi kopš 21.10.2026.Kas jādara tagad →

Droši gadījuma skaitļi un tokeni PHP ar random_bytes

Kā izstrādātāji PHP veido tokenus paroles atiestatīšanas saitēm, API atslēgām un vienreizējiem kodiem: kriptogrāfiski droši avoti, pietiekams garums, glabāšana kā jaucējkods un ierobežota derīgums.

Aktuāls uz 5 min lasīšanas laiksDroša izstrāde

Īsi sakot: Drošus tokenus PHP veidojat ar random_bytes(), piemēram, 32 baitus, ko ar bin2hex() vai Base64-URL pārvēršat tekstā. Gadījuma skaitļiem diapazonā ir random_int(), kodiem no fiksēta rakstzīmju krājuma kopš PHP 8.3 Random\Randomizer::getBytesFromString(). Tokenus glabājiet kā jaucējkodu, ierobežojiet to derīgumu, lietojiet tikai vienreiz un salīdziniet ar hash_equals().

Saite, ko pārāk viegli uzminēt

„Aizmirsāt paroli?" Viens klikšķis, un sistēma nosūta saiti ar garu kodu. Kas saiti zina, var iestatīt paroli. Tieši tāpēc šim kodam jābūt neparedzamam. Ja tā nav, uzbrucējam nav vajadzīga ne pastkaste, ne vecā parole, tikai pietiekami daudz mēģinājumu.

PHP lietojumprogrammu pārbaudēs pārsteidzoši bieži atrodami tokeni, kas veidoti no pulksteņa laika, lietotāja ID vai vienkārša gadījuma skaitļu ģeneratora. Tie izskatās gari un nejauši, bet tādi nav. Tas pats attiecas uz API atslēgām, ielūguma saitēm, dokumentu kopīgošanas saitēm, apstiprinājuma kodiem un CSRF tokeniem.

Risks ir taustāms: paredzama atiestatīšanas saite ļauj pārņemt katru kontu, kura e-pasta adrese ir zināma, un īpašnieks neko nemana, kamēr pats vairs nevar pieteikties.

PHP kopš versijas 7.0 piedāvā vienkāršu un drošu risinājumu. Tas tikai konsekventi jālieto.

random_bytes PHP: no kurienes nāk droša nejaušība

random_bytes() saskaņā ar PHP rokasgrāmatu atdod kriptogrāfiski drošus gadījuma baitus, kas der arī ilgdzīvojošiem noslēpumiem, piemēram, atslēgām. Funkcija izmanto operētājsistēmas nejaušības avotu, Linux piemēram getrandom(), Windows CNG saskarni. Ja piemērotu avotu neatrod, tā kopš PHP 8.2 met Random\RandomException.

Pārskats rāda, kuras funkcijas der noslēpumiem:

Funkcija Piemērota tokeniem? Piezīme
random_bytes() jā droši baiti, kopš PHP 7.0
random_int() jā drošs skaitlis diapazonā, kopš PHP 7.0
Random\Randomizer ar Secure dzinēju jā saskaņā ar rokasgrāmatu Secure ir noklusējuma dzinējs
rand(), mt_rand() nē paredzami, tikai ar drošību nesaistītiem mērķiem
uniqid() nē balstīts uz laiku, paredzēts unikalitātei
md5(time()), sha1($id) nē ievadi var uzminēt, jaucējkods to nemaina
str_shuffle(), array_rand() nē neizmanto drošu ģeneratoru

Tokenu izveide: garums un kodējums

32 nejauši baiti atbilst 256 bitiem un gandrīz jebkuram mērķim ir vairāk nekā pietiekami. Pārraidei saitēs vai galvenēs tokens jāpārvērš tekstā. Ierasti ir divi varianti:

<?php
declare(strict_types=1);

// Heksadecimāli: 64 rakstzīmes, tikai 0-9 un a-f
$tokenHex = bin2hex(random_bytes(32));

// Base64-URL: 43 rakstzīmes, droši URL bez papildu kodēšanas
$tokenUrl = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');

// Gadījuma skaitlis diapazonā, piemēram, izvēlei
$index = random_int(0, 9);

// No PHP 8.3: sešciparu skaitļu kods, piemēram, apstiprinājumam ar SMS
$randomizer = new \Random\Randomizer();
$code = $randomizer->getBytesFromString('0123456789', 6);

Īsi skaitļu kodi ir attaisnojami tikai tad, ja mēģinājumu skaits ir stingri ierobežots un kods pēc dažām minūtēm zaudē derīgumu. Saitēm, ko sūta pa e-pastu, vienmēr ņemiet garo tokenu.

API atslēgām, ko partneri vai savas sistēmas lieto ilgstoši, pierādījušies divi papildinājumi. Īss, nevis slepens prefikss pirms nejaušās daļas, piemēram, videi vai mērķim, atvieglo atpazīšanu žurnālos un atradumos koda repozitorijos. Un atslēga tiek parādīta tikai vienreiz izveidošanas brīdī; pēc tam sistēma zina tikai tās jaucējkodu. Ja atslēga ir pazaudēta, tā tiek aizstāta, nevis atjaunota.

Piesardzība ar paštaisītiem tokeniem, kas satur informāciju, piemēram, lietotāja ID un derīguma laiku Base64. Kodējums nav aizsardzība: ikviens šādus tokenus var nolasīt un mainīt. Ja tokenam jānes dati, tas jāparaksta ar slepenu atslēgu, piemēram, ar hash_hmac('sha256', ...), un paraksts jāpārbauda ar hash_equals(). Sarežģītākos gadījumos pārbaudītas bibliotēkas ir labākas par paša konstrukcijām.

Tokenu glabāšana, pārbaude un derīguma beigas

Droši izveidots tokens ir tikai puse darba. Vismaz tikpat svarīgi ir tas, kā lietojumprogramma ar to apietas. Pierādījusies pieeja atiestatīšanas un ielūguma saitēm:

  1. Glabāt tikai jaucējkodu: datubāzē ir hash('sha256', $token), pats tokens pie lietotāja nonāk tikai pa saiti.
  2. Ierobežot derīgumu: līdzi saglabāt derīguma beigu laiku un pārbaudīt izmantojot.
  3. Lietot vienreiz: pēc sekmīgas lietošanas ierakstu dzēst vai atzīmēt kā izlietotu.
  4. Anulēt izmaiņu gadījumā: pēc paroles maiņas dzēst visus konta atvērtos atiestatīšanas tokenus.
  5. Salīdzināt nemainīgā laikā: kur tokenu tieši salīdzina ar saglabātu vērtību, lietot hash_equals(), saglabāto vērtību norādot kā pirmo parametru.
<?php
// Izveidot
$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 sūtīt tikai saitē, $eintrag saglabāt

// Izmantot
$gesucht = hash('sha256', (string) ($_GET['token'] ?? ''));
// Ierakstu meklēt ar Prepared Statement pēc token_hash,
// pārbaudīt derīgumu, tad ierakstu nekavējoties dzēst

Ierobežojiet arī atiestatīšanas pieprasījumu skaitu katram kontam un laika posmam, lai funkciju nevarētu ļaunprātīgi izmantot masveida e-pastu sūtīšanai. Atbildei uz atiestatīšanas pieprasījumu turklāt nevajadzētu atklāt, vai norādītajai adresei vispār ir konts.

Ātrs jaucējkods, piemēram, SHA-256, šeit pietiek, jo pats tokens jau ir garš un nejaušs. Parolēm, ko izvēlas cilvēki, tas neattiecas; tur vajadzīgas lēnas metodes, kā skaidro Paroļu droša glabāšana PHP.

Padoms

Tokeniem nav vietas žurnālfailos. Pārbaudiet, vai jūsu tīmekļa serveris žurnālā raksta pilnus URL ar vaicājuma parametriem un vai kļūdu ziņojumi līdzi sūta pieprasījuma datus.

Pēc incidenta: tokenu un atslēgu atjaunošana

Kas serverī varēja izpildīt kodu, tam parasti bija piekļuve arī saglabātajiem tokeniem, API atslēgām un sesiju datiem. Ja pēc PHP2Shell vai cita incidenta pastāv aizdomas par ielaušanos, atjaunojiet tāpēc ne tikai paroles, bet arī ilgdzīvojošus tokenus un atslēgas: API piekļuves, parakstu atslēgas sīkdatnēm vai saitēm, piekļuves maksājumu un piegādes pakalpojumiem.

Sesijas ir īpašs gadījums: PHP sesiju ID izveido pats. Kā to iestatījumus nodrošināt un sesijas pēc incidenta beigt, apraksta ceļvedis Sesiju nostiprināšana PHP. Veidlapu tokeniem pret viltotiem pieprasījumiem skatiet CSRF aizsardzība PHP lietojumprogrammās. Citus iebūvētos rīkus iepazīstina PHP iebūvētās drošības funkcijas.

Kā PHP SOS palīdz konkrēti?

  • Mēs jūsu lietojumprogrammā atrodam visas vietas, kur tiek veidoti tokeni, kodi un atslēgas, un aizstājam nedrošus avotus.
  • Mēs pārbaudām atiestatīšanas, ielūguma un kopīgošanas saišu glabāšanu, derīguma beigas un vienreizēju lietošanu.
  • Mēs gādājam, lai tokeni nenonāktu žurnālos, kļūdu ziņojumos vai analīzes rīkos.
  • Pēc incidenta mēs kopā ar jums plānojam tokenu un atslēgu sakārtotu nomaiņu.

Savas PHP programmatūras pārbaudi piedāvājam kā drošības auditu un penetrācijas testu.

Biežāk uzdotie jautājumi

Kāpēc mt_rand nav piemērots tokeniem?

mt_rand un rand paredzēti statistikai un spēlēm, nevis noslēpumiem. To izvadi zināmos apstākļos var paredzēt. Visam, ko uzbrucējs nedrīkst uzminēt, lietojiet random_bytes vai random_int.

Cik garam jābūt tokenam?

Paroles atiestatīšanas saitēm, ielūgumiem vai API atslēgām 32 nejauši baiti ir solīda izvēle, kā heksadecimāls teksts 64 rakstzīmes. 16 baiti, tas ir, 128 biti, ir izplatīta apakšējā robeža.

Vai uniqid ir drošs tokens?

Nē. uniqid balstās uz pašreizējo laiku un paredzēts unikalitātei, nevis slepenībai. Arī kombinācijas, piemēram, md5 no laika vai lietotāja ID, ir paredzamas.

Vai atiestatīšanas tokeni datubāzē jāšifrē?

Tokena vietā glabājiet tā jaucējkodu, piemēram, ar SHA-256. Kas lasa datubāzi, ar to nevar izveidot derīgas saites. Tā kā pats tokens jau ir garš un nejaušs, lēna metode kā parolēm nav vajadzīga.

Kas notiek, ja sistēma neatrod gadījuma avotu?

random_bytes un random_int kopš PHP 8.2 met Random\RandomException. Uztveriet šo gadījumu un pārtrauciet darbību, nevis pārejiet uz vājāku avotu.

Avoti

Tūlītējs kontakts

Katra stunda ir svarīga. Rakstiet mums tūlīt.

Īsi aprakstiet sistēmu, PHP versiju un novēroto. Mēs sazināsimies, noskaidrosim situāciju un strādāsim attālināti, līdz incidents ir slēgts un dokumentēts.

Tūlītēja palīdzība