Ako vybrať náhodnú hodnotu z poľa v PHP. Php random: Ako získať náhodnú hodnotu z poľa Php náhodnej hodnoty


Náhodné hodnoty sú v PHP všade. Vo všetkých rámcoch, v mnohých knižniciach. Pravdepodobne ste sami napísali veľa kódu, ktorý používa náhodné hodnoty na generovanie tokenov a solí a ako vstup pre funkcie. Náhodné hodnoty tiež hrajú dôležitú úlohu pri riešení rôznych problémov:

  1. Náhodný výber možností zo skupiny alebo radu známych možností.
  2. Na generovanie inicializačných vektorov pre šifrovanie.
  3. Na generovanie nepredvídateľných tokenov alebo jednorazových hodnôt počas autorizácie.
  4. Na generovanie jedinečných identifikátorov, ako sú ID relácie.

Vo všetkých týchto prípadoch existuje charakteristická zraniteľnosť. Ak útočník uhádne alebo predpovedá výstup vášho generátora náhodných čísel (RNG) alebo generátora pseudonáhodných čísel (PRNG), bude schopný vypočítať tokeny, soli, nonces a kryptografické inicializačné vektory vytvorené týmto generátorom. Preto je veľmi dôležité generovať kvalitné náhodné hodnoty, teda také, ktoré je extrémne ťažké predpovedať. Nikdy nerobte tokeny na obnovenie hesla, tokeny CSRF, kľúče API, nonces alebo autorizačné tokeny predvídateľné!


S náhodnými hodnotami v PHP sú spojené ďalšie dve potenciálne zraniteľnosti:

  1. Zverejnenie informácií.
  2. Nedostatočná entropia.

V tejto súvislosti sa „zverejnenie informácií“ vzťahuje na únik vnútorného stavu generátora pseudonáhodných čísel – jeho počiatočnej hodnoty. Takéto úniky môžu výrazne uľahčiť predpovedanie budúcich výstupov PRNG.


„Nedostatok entropie“ opisuje situáciu, keď je variabilita počiatočného vnútorného stavu (semena) PRNG alebo jeho výstupu taká malá, že celý rozsah možných hodnôt je relatívne ľahko hrubou silou. Nie príliš dobrá správa pre PHP programátorov.


Na obe zraniteľnosti sa pozrieme podrobne s príkladmi scenárov útoku. Najprv si však poďme pochopiť, čo je to vlastne náhodná hodnota, pokiaľ ide o programovanie v PHP.

Čo robia náhodné hodnoty?

Zmätok o účele náhodných premenných je znásobený všeobecným nedorozumením. Nepochybne ste už počuli o rozdiele medzi kryptograficky silnými náhodnými hodnotami a vágnymi „jedinečnými“ hodnotami „na iné použitie“. Hlavným dojmom je, že náhodné hodnoty používané v kryptografii vyžadujú kvalitnú náhodnosť (alebo presnejšie vysokú entropiu), zatiaľ čo hodnoty pre iné aplikácie si vystačia s menšou entropiou. Tento dojem považujem za falošný a kontraproduktívny. Skutočný rozdiel medzi nepredvídateľnými náhodnými hodnotami a hodnotami potrebnými pre triviálne úlohy spočíva v tom, že ich predvídateľnosť nemá škodlivé následky. Toto vylučuje kryptografiu z úvahy úplne. Inými slovami, ak použijete náhodnú hodnotu v netriviálnom probléme, mali by ste automaticky zvoliť oveľa silnejšie RNG.


Sila náhodných hodnôt je určená entropiou vynaloženou na ich generovanie. Entropia je miera neistoty vyjadrená v "bitoch". Napríklad, ak vezmem binárny bit, jeho hodnota môže byť 0 alebo 1. Ak útočník nepozná presnú hodnotu, potom máme entropiu 2 bity (t. j. hod mincou). Ak útočník vie, že hodnota je vždy 1, potom máme entropiu 0 bitov, pretože predvídateľnosť je antonymom neistoty. Počet bitov sa tiež môže pohybovať od 0 do 2. Napríklad, ak je 99 % času binárny bit 1, potom môže byť entropia o niečo väčšia ako 0. Takže čím viac nedefinovaných binárnych bitov vyberieme, tým lepšie.


V PHP to možno vidieť jasnejšie. Funkcia mt_rand() generuje náhodné hodnoty, vždy ide o čísla. Nezobrazuje písmená, špeciálne znaky ani iné významy. To znamená, že na každý bajt má útočník oveľa menej odhadov, t. j. nízku entropiu. Ak nahradíme mt_rand() čítaním bajtov zo zdroja Linux /dev/random , dostaneme skutočne náhodné bajty: generujú sa na základe šumu generovaného ovládačmi systémových zariadení a inými zdrojmi. Je zrejmé, že táto možnosť je oveľa lepšia, pretože poskytuje podstatne viac bitov entropie.


O nežiaducosti mt_rand() svedčí aj fakt, že nejde o generátor skutočne náhodných, ale pseudonáhodných čísel, alebo, ako sa tiež nazýva, o deterministický generátor náhodných binárnych sekvencií (Deterministic Random Bit Generator, DRBG ). Implementuje algoritmus nazývaný Mersenne Twister, ktorý generuje čísla distribuované takým spôsobom, že výsledok je blízky výsledku skutočného generátora náhodných čísel. mt_rand() používa iba jednu náhodnú hodnotu - seed, na základe ktorého pevný algoritmus generuje pseudonáhodné hodnoty.


Pozrite sa na tento príklad, môžete si to vyskúšať sami:


mt_srand(1361152757.2); pre ($i=1; $i< 25; $i++) { echo mt_rand(), PHP_EOL; }

Toto je jednoduchá slučka, ktorá sa vykoná po tom, čo bola funkcii PHP Mersenne vortex pridelená počiatočná, prednastavená hodnota. Bol získaný z výstupu funkcie uvedenej ako príklad v dokumentácii pre mt_srand() a pomocou aktuálnych sekúnd a mikrosekúnd. Ak spustíte vyššie uvedený kód, zobrazí sa 25 pseudonáhodných čísel. Vyzerajú náhodne, žiadne náhody, všetko je v poriadku. Spustite kód znova. Všimli ste si niečo? Konkrétne: zobrazia sa ROVNAKÉ čísla. Poďme to spustiť tretí, štvrtý, piaty raz. V starších verziách PHP môže byť výsledok odlišný, ale to nie je problém, pretože je to spoločné pre všetky moderné verzie PHP.


Ak útočník získa počiatočnú hodnotu takéhoto PRNG, bude schopný predpovedať celý výstup mt_rand() . Takže ochrana počiatočnej hodnoty je nanajvýš dôležitá. Ak ho stratíte, už nemáte právo generovať náhodné hodnoty...


Počiatočnú hodnotu môžete vygenerovať jedným z dvoch spôsobov:

  • manuálne pomocou funkcie mt_srand()
  • budete ignorovať mt_srand() a necháte PHP, aby ho vygenerovalo automaticky.

Druhá možnosť je vhodnejšia, ale aj dnes staršie aplikácie často zdedia použitie mt_srand(), a to aj po portovaní na modernejšie verzie PHP.


To zvyšuje riziko, že útočník získa späť počiatočnú hodnotu (Seed Recovery Attack), čo mu poskytne dostatok informácií na predpovedanie budúcich hodnôt. V dôsledku toho sa každá aplikácia po takomto úniku stane zraniteľnou voči útoku na sprístupnenie informácií. Toto je skutočná zraniteľnosť, napriek jej zjavne pasívnej povahe. Únik informácií o lokálnom systéme môže útočníkovi pomôcť pri následných útokoch, ktoré porušia princíp obrany do hĺbky.

Náhodné hodnoty v PHP

PHP používa tri PRNG a ak útočník získa prístup k semenám použitým v ich algoritmoch, bude schopný predpovedať výsledky ich práce:

  1. Lineárny kongruenciálny generátor (LCG), lcg_value() .
  2. Mersennov vír, mt_rand() .
  3. Lokálne podporovaná funkcia C rand() .

Tieto generátory sa používajú aj interne, pre funkcie ako array_rand() a uniqid() . To znamená, že útočník môže predpovedať výstup týchto a ďalších funkcií, ktoré používajú interné PRNG PHP, ak získajú všetky požadované semená. Znamená to tiež, že nemôžete zlepšiť svoju obranu zmätením útočníka viacerými volaniami do generátorov. To platí najmä pre aplikácie s otvoreným zdrojom. Útočník je schopný predpovedať VŠETKY výstupy pre akúkoľvek počiatočnú hodnotu, ktorú pozná.


Na zlepšenie kvality generovaných náhodných hodnôt pre netriviálne úlohy potrebuje PHP externé zdroje entropie poskytované operačným systémom. V Linuxe sa zvyčajne používa /dev/urandom, môžete ho čítať priamo alebo k nemu pristupovať nepriamo pomocou funkcií openssl_pseudo_random_bytes() alebo mcrypt_create_iv(). Obaja môžu používať kryptograficky bezpečný generátor pseudonáhodných čísel (CSPRNG) na Windows, ale v PHP v užívateľskom priestore zatiaľ neexistuje priama metóda na získanie údajov z tohto generátora bez rozšírení poskytovaných týmito funkciami. Inými slovami, uistite sa, že vaša serverová verzia PHP má povolené rozšírenie OpenSSL alebo Mcrypt.


/dev/urandom je PRNG, ale často získava nové semená zo zdroja s vysokou entropiou /dev/random . To z neho robí pre útočníka nezaujímavý cieľ. Snažíme sa vyhnúť čítaniu priamo z /dev/random, pretože je to blokujúci zdroj. Ak mu dôjde entropia, všetky čítania budú zablokované, kým opäť nezíska dostatok entropie zo systémového prostredia. Aj keď pre najdôležitejšie úlohy by ste mali použiť /dev/random .


To všetko nás vedie k pravidlu:


Všetky procesy, ktoré zahŕňajú použitie netriviálnych náhodných čísel MUSIA používať openssl_pseudo_random_bytes(). Prípadne sa môžete pokúsiť čítať bajty priamo z /dev/urandom. Ak nefunguje ani jedna možnosť a nemáte na výber, potom MUSÍTE vygenerovať hodnotu intenzívnym zmiešaním údajov z viacerých dostupných zdrojov náhodných alebo tajných hodnôt.

Základnú implementáciu tohto pravidla nájdete v referenčnej knižnici SecurityMultiTool. Ako obvykle, interné PHP radšej sťažujú život programátorom namiesto priameho zahrnutia bezpečných riešení do jadra PHP.


Dosť bolo teórie, teraz sa pozrime, ako môžete zaútočiť na aplikáciu, vyzbrojenú vyššie uvedeným.

Útok na generátory náhodných čísel v PHP

Z viacerých dôvodov PHP používa PRNG na riešenie netriviálnych problémov.


Funkcia openssl_pseudo_random_bytes() bola dostupná len v PHP 5.3. Na Windows to spôsobovalo problémy s blokovaním, kým nevyšla verzia 5.3.4. V PHP 5.3 začala funkcia mcrypt_create_iv() vo Windowse podporovať zdroj MCRYPT_DEV_URANDOM. Predtým Windows podporoval iba MCRYPT_RAND - v podstate rovnaký systém PRNG, ktorý interne používa funkcia rand(). Ako môžete vidieť, pred PHP 5.3 bolo veľa medzier, takže mnohé staršie aplikácie napísané v predchádzajúcich verziách možno neprešli na silnejšie PRNG.


Výber rozšírení Openssl a Mcrypt je podľa vášho uváženia. Keďže sa nemožno spoľahnúť, že budú dostupné dokonca aj na serveroch s PHP 5.3, aplikácie často používajú PRNG zabudované v PHP ako záložný zdroj na generovanie netriviálnych náhodných hodnôt.


Ale v oboch prípadoch máme netriviálne problémy, ktoré používajú náhodné hodnoty generované PRNG so semenami s nízkou entropiou. To nás robí zraniteľnými voči útokom na vyhľadávanie. Pozrime sa na jednoduchý príklad.


Predstavme si, že sme našli online aplikáciu, ktorá používa nasledujúci kód na generovanie tokenov, ktoré sa používajú v rôznych úlohách v celej aplikácii:


$token = hash("sha512", mt_rand());

Existujú zložitejšie spôsoby generovania tokenov, ale toto je dobrá možnosť. Existuje len jedno volanie funkcie mt_rand(), hašované pomocou SHA512. V praxi, ak sa programátor rozhodne, že funkcie náhodných hodnôt PHP sú „dostatočne náhodné“, potom s najväčšou pravdepodobnosťou zvolí zjednodušujúci prístup, kým sa nespomína slovo „kryptografia“. Napríklad nekryptografické prípady zahŕňajú prístupové tokeny, tokeny CSRF, jednorazové hodnoty API a tokeny na obnovenie hesla. Skôr než budem pokračovať, podrobne popíšem celý rozsah zraniteľnosti tejto aplikácie, aby ste lepšie pochopili, prečo sú aplikácie zraniteľné.

Charakteristika zraniteľnej aplikácie

Toto nie je úplný zoznam. V praxi sa zoznam charakteristík môže líšiť!

1. Server používa mod_php, ktorý pri použití s ​​KeepAlive umožňuje obsluhovať viacero požiadaviek rovnakým procesom PHP

Je to dôležité, pretože generátory náhodných čísel v PHP sa nasadzujú iba raz za proces. Ak môžeme urobiť dve alebo viac požiadaviek na proces, potom použije rovnakú počiatočnú hodnotu. Podstatou útoku je využiť expanziu jedného tokenu na extrakciu hodnoty seed, ktorá je potrebná na predpovedanie ďalšieho tokenu vygenerovaného na základe ROVNAKEJ hodnoty seed (t.j. v rovnakom procese). Keďže mod_php je ideálny na použitie viacerých dotazov na získanie súvisiacich náhodných hodnôt, niekedy môže byť možné získať viacero hodnôt súvisiacich s mt_rand() iba jedným dotazom. Vďaka tomu sú akékoľvek požiadavky mod_php nadbytočné. Napríklad časť entropie použitej na generovanie zdroja pre mt_rand() môže uniknúť cez ID relácie alebo výstupné hodnoty v tej istej požiadavke.

2. Server odhalí tokeny CSRF, tokeny na obnovenie hesla alebo tokeny na potvrdenie účtu vygenerované na základe tokenov mt_rand()

Aby sme extrahovali seed hodnotu, musíme priamo skontrolovať číslo generované generátormi v PHP. A nezáleží ani na tom, ako sa používa. Môžeme ho extrahovať z akejkoľvek dostupnej hodnoty, či už je to výstup mt_rand(), hash CSRF, alebo token overenia účtu. Vhodné sú aj nepriame zdroje, v ktorých náhodná hodnota určuje iné správanie na výstupe, čo prezrádza práve túto hodnotu. Hlavným obmedzením je, že musí pochádzať z rovnakého procesu, ktorý generuje druhý token, ktorý sa snažíme predpovedať. A toto je zraniteľnosť týkajúca sa „sprístupnenia informácií“. Ako čoskoro uvidíme, únik PRNG výstupu môže byť mimoriadne nebezpečný. Všimnite si, že zraniteľnosť nie je obmedzená na jednu aplikáciu: môžete čítať výstup PRNG v jednej aplikácii na serveri a použiť ho na určenie výstupu v inej aplikácii na rovnakom serveri, pokiaľ obe používajú rovnaký proces PHP.

3. Známy slabý algoritmus generovania tokenov

Môžete si to vypočítať:

  • po ponorení sa do zdrojov aplikácie s otvoreným zdrojovým kódom,
  • podplácanie zamestnanca s prístupom k osobnému zdrojovému kódu,
  • nájdenie bývalého zamestnanca, ktorý prechováva zášť voči svojmu bývalému zamestnávateľovi,
  • alebo jednoducho hádať, aký algoritmus tam môže byť.

Niektoré metódy generovania tokenov sú zreteľnejšie, niektoré sú populárnejšie. Skutočne slabé generovanie znamená použiť jeden z PHP generátorov náhodných čísel (napr. mt_rand()), slabú entropiu (žiadne iné zdroje nedefinovaných údajov) a/alebo slabé hashovanie (napr. MD5 alebo žiadne hashovanie). Vyššie uvedený príklad kódu má znaky slabej metódy generovania. Použil som tiež hashovanie SHA512, aby som ukázal, že maskovanie je vždy neuspokojivé riešenie. SHA512 je slabé hashovanie, pretože sa rýchlo počíta, čo znamená, že útočník môže hrubo vynútiť vstupné údaje na akomkoľvek CPU alebo GPU neuveriteľnou rýchlosťou. A nezabudnite, že stále platí aj Moorov zákon, čo znamená, že rýchlosť hrubej sily sa bude zvyšovať s každou novou generáciou CPU/GPU. Preto musia byť heslá hašované pomocou nástrojov, ktorých prelomenie trvá pevne stanovený čas, bez ohľadu na výkon procesora alebo Mooreov zákon.

Prevedenie útoku

Náš útok je celkom jednoduchý. V rámci pripojenia k procesu PHP uskutočníme rýchlu reláciu a odošleme dve samostatné HTTP požiadavky (požiadavka A a požiadavka B). Relácia bude držaná serverom, kým nebude prijatá druhá požiadavka. Požiadavka A je zameraná na získanie nejakého dostupného tokenu, ako je CSRF, token na obnovenie hesla (zaslaný útočníkovi poštou) alebo niečo podobné. Nezabudnite na ďalšie funkcie, ako je inline značkovanie používané v žiadostiach o ľubovoľné ID atď. Pôvodný token budeme trápiť, kým nám nedá svoju počiatočnú hodnotu. Toto všetko je súčasťou útoku na obnovu semena: kde semeno má takú malú entropiu, že ho možno hrubo vynútiť alebo vyhľadať v vopred vypočítanej dúhovej tabuľke.


Dotaz B vyrieši zaujímavejší problém. Požiadame o obnovenie hesla lokálneho správcu. Toto spustí generovanie tokenu (pomocou náhodného čísla založeného na rovnakom semene, ktoré vytiahneme s požiadavkou A, ak sú obe požiadavky úspešne odoslané rovnakému procesu PHP). Tento token bude uložený v databáze v očakávaní okamihu, keď správca použije odkaz na obnovenie hesla, ktorý mu bol zaslaný e-mailom. Ak dokážeme extrahovať počiatočnú hodnotu pre token z požiadavky A, potom vieme, ako sa generuje token z požiadavky B, môžeme predpovedať token na obnovenie hesla. To znamená, že môžeme nasledovať odkaz na obnovenie skôr, ako správca prečíta list!


Tu je postupnosť udalostí:

  1. Pomocou požiadavky A získame token a spätne ho analyzujeme, aby sme vypočítali počiatočnú hodnotu.
  2. Pomocou požiadavky B získame token vygenerovaný na základe rovnakej počiatočnej hodnoty. Tento token je uložený v databáze aplikácie pre budúce resetovanie hesla.
  3. Prelomíme hash SHA512, aby sme získali náhodné číslo vygenerované serverom.
  4. Pomocou výslednej náhodnej hodnoty hrubou silou vytlačíme počiatočnú hodnotu, ktorá bola s jej pomocou vygenerovaná.
  5. Semeno používame na výpočet série náhodných hodnôt, ktoré môžu pravdepodobne tvoriť základ tokenu na obnovenie hesla.
  6. Tento token (tokeny) používame na obnovenie hesla správcu.
  7. Získame prístup k správcovskému účtu, zábavu a výhody. No aspoň sa bavíme.

Začnime hackovať...

Hackovanie aplikácií krok za krokom

Krok 1. Požiadajte A o získanie tokenu

Predpokladáme, že cieľový token a token na obnovenie hesla závisia od výstupu mt_rand() . Preto si ho treba vybrať. V aplikácii v našom imaginárnom scenári sú všetky tokeny generované rovnakým spôsobom, takže token CSRF môžeme jednoducho extrahovať a uložiť na neskôr.

Krok 2. Vykonajte požiadavku B na získanie tokenu na obnovenie hesla vygenerovaného pre účet správcu

Táto žiadosť je jednoduchým odoslaním formulára na obnovenie hesla. Token bude uložený v databáze a zaslaný používateľovi poštou. Tento token musíme správne vypočítať. Ak sú špecifikácie servera presné, požiadavka B používa rovnaký proces PHP ako požiadavka A. Preto volania funkcie mt_rand() budú používať rovnakú počiatočnú hodnotu v oboch prípadoch. Požiadavku A môžete dokonca použiť na zachytenie tokenu CSRF formulára na obnovenie, aby ste umožnili odoslanie v záujme zefektívnenia postupu (vyhýbanie sa spiatočnej ceste).

Krok 3. Hacknite hash SHA512 tokenu prijatého z požiadavky A

SHA512 vzbudzuje úctu medzi programátormi: má najväčší počet v celej rodine algoritmov SHA-2. Existuje však jeden problém s metódou generovania tokenov našej obete - náhodné hodnoty sú obmedzené iba na čísla (t. j. stupeň neistoty alebo entropie je zanedbateľný). Ak skontrolujete výstup mt_getrandmax() , zistíte, že najväčšie náhodné číslo, ktoré môže mt_rand() vygenerovať, je 2,147 miliardy a nejaká zmena. Tento obmedzený počet funkcií robí SHA512 zraniteľným voči hrubej sile.


Len ma neberte za slovo. Ak máte samostatnú grafickú kartu z jednej z najnovších generácií, môžete ísť nasledujúcim spôsobom. Keďže hľadáme jediný hash, rozhodol som sa použiť úžasný nástroj hrubej sily – hashcat-lite. Toto je jedna z najrýchlejších verzií hashcat a je dostupná pre všetky hlavné operačné systémy vrátane Windowsu.


Na vygenerovanie tokenu použite tento kód:


$rand = mt_rand(); echo "Náhodné číslo: ", $rand, PHP_EOL; $token = hash("sha512", $rand); echo "Token: ", $token, PHP_EOL;

Tento kód reprodukuje token z požiadavky A (obsahuje náhodné číslo, ktoré potrebujeme a je skrytý v hash SHA512) a spustí ho cez hashcat:


./oclHashcat-lite64 -m1700 --pw-min=1 --pw-max=10 -1?d -o ./seed.txt ?d?d?d?d?d?d?d?d?d?d

Čo znamenajú všetky tieto možnosti:

  • -m1700: Určuje hashovací algoritmus, kde 1700 znamená SHA512.
  • --pw-min=1: Definuje minimálnu vstupnú dĺžku hašovanej hodnoty.
  • --pw-max=10: Definuje maximálnu vstupnú dĺžku hašovanej hodnoty (10 pre mt_rand()).
  • -1?d: určuje, že potrebujeme vlastný slovník iba čísel (t. j. 0-9).
  • -o ./seed.txt: súbor na zapisovanie výsledkov. Na obrazovke sa nič nezobrazuje, takže nezabudnite nastaviť túto možnosť!
  • ?d?d?d?d?d?d?d?d?d?d: maska, ktorá špecifikuje formát, ktorý sa má použiť (všetky číslice do maximálne 10).

Ak všetko funguje správne a váš GPU sa neroztopí, Hashcat vypočíta hashované náhodné číslo za pár minút. Áno, minúty. Predtým som vysvetlil, ako funguje entropia. Presvedčte sa sami. Funkcia mt_rand() má tak málo možností, že SHA512 hash všetkých hodnôt možno skutočne vypočítať vo veľmi krátkom čase. Takže nemalo zmysel hašovať výstup mt_rand() .

Krok 4. Obnovte počiatočnú hodnotu pomocou čerstvo napadnutého náhodného čísla

Ako sme videli vyššie, extrahovanie akejkoľvek hodnoty vygenerovanej pomocou mt_rand() z SHA512 trvá len pár minút. Vyzbrojení náhodnou hodnotou môžeme spustiť ďalší nástroj hrubej sily – php_mt_seed. Táto malá utilita vezme výstup mt_rand() a po hrubej sile vypočíta počiatočnú hodnotu, z ktorej mohla byť vygenerovaná analýza. Stiahnite si aktuálnu verziu, skompilujte a spustite. Ak máte problémy s kompiláciou, skúste staršiu verziu (s novými som mal problémy s virtuálnymi prostrediami).


./php_mt_seed

Môže to trvať o niečo dlhšie ako praskanie SHA512, pretože sa to robí na CPU. Na slušnom procesore utilita nájde celý možný rozsah počiatočnej hodnoty za pár minút. Výsledkom je jedna alebo viac možných hodnôt (to znamená hodnoty, ktoré mohli byť použité na vytvorenie náhodného čísla). Opäť vidíme výsledok slabej entropie, len tentoraz v súvislosti s generovaním počiatočných hodnôt PHP pre funkciu Mersenne vortex. Na to, ako sa tieto hodnoty generovali, sa pozrieme neskôr, takže uvidíte, prečo je možné použiť hrubú silu tak rýchlo.


Takže predtým sme používali jednoduché hackerské nástroje dostupné na webe. Sú zamerané na volania mt_rand(), ale ilustrujú myšlienku, ktorú možno použiť na iné scenáre (napríklad sekvenčné volania mt_rand() pri generovaní tokenov). Majte tiež na pamäti, že rýchlosť hackovania nebráni generovaniu dúhových tabuliek, ktoré zohľadňujú špecifické prístupy generovania tokenov. Tu je ďalší nástroj, ktorý využíva zraniteľnosti mt_rand() a je napísaný v Pythone.

Krok 5. Vygenerujte možné tokeny na obnovenie hesla účtu správcu

Predpokladajme, že v rámci požiadaviek A a B boli odoslané iba dve požiadavky na mt_rand() . Teraz začnime predpovedať tokeny pomocou predtým vypočítaných možných počiatočných hodnôt:


function Predikcia($seed) ( /** * Odovzdať počiatočnú hodnotu PRNG */ mt_srand($seed); /** * Preskočiť volanie funkcie z požiadavky A */ mt_rand(); /** * Predpovedať a vrátiť jeden vygenerovaný v tokene požiadavky B */ $token = hash("sha512", mt_rand()); return $token; )

Táto funkcia predpovedá resetovací token pre každý možný zdroj.

Kroky 6 a 7: Obnovte heslo účtu správcu a bavte sa!

Teraz musíte zhromaždiť adresu URL obsahujúcu token, ktorý vám umožní obnoviť heslo správcu z dôvodu zraniteľnosti aplikácie a získať prístup k vášmu účtu. Možno zistíte, že nefiltrované HTML môžete uverejňovať na fóre alebo v článku (bežné porušenie princípu hĺbkovej obrany). To vám umožní vykonať rozsiahly XSS útok na všetkých ostatných používateľov aplikácie, infikovať ich počítače malvérom a monitorovať Man-In-The-Browser. Vážne, prečo sa zastaviť len pri získavaní prístupu? Zmyslom týchto zdanlivo pasívnych a nie príliš nebezpečných zraniteľností je pomôcť útočníkovi pomaly preniknúť tam, kde môže konečne dosiahnuť svoj hlavný cieľ. Hackovanie je ako hranie arkádovej bojovej hry, kde musíte rýchlo stlačiť správnu kombináciu, aby ste uvoľnili sériu silných útokov.

Analýza po útoku

Vyššie uvedený scenár a jednoduchosť krokov by vám mali jasne ukázať nebezpečenstvo mt_rand() . Riziká sú také zrejmé, že akékoľvek slabo skryté výstupné hodnoty mt_rand() prístupné útočníkovi v akejkoľvek forme teraz môžeme považovať za zraniteľnosť „odhalenia informácií“.


Navyše tento príbeh má aj druhú stránku. Napríklad, ak ste závislí na knižnici, ktorá nevinne používa mt_rand() na niektoré dôležité úlohy, a to aj bez uvedenia výsledných hodnôt, potom použitím „netesného“ tokenu pre svoje potreby kompromitujete túto knižnicu. A to je problém, pretože knižnica alebo rámec nerobí nič na zmiernenie útoku obnovenia počiatočnej hodnoty. Mali by sme viniť používateľa za únik hodnôt mt_rand () alebo knižnicu za to, že nepoužila lepšie náhodné hodnoty?


V skutočnosti sú obaja celkom vinní. Knižnica by si nemala zvoliť mt_rand() (alebo akýkoľvek iný zdroj slabej entropie) pre dôležité problémy ako jediný zdroj náhodných hodnôt. A používateľ by nemal písať kód, ktorý prepúšťa hodnoty mt_rand(). Takže áno, môžete začať ukazovať obviňujúcim prstom na negramotné príklady použitia mt_rand() , aj keď to nevedie k priamym únikom.


Nie sú to len zraniteľné miesta pri zverejňovaní informácií, ktorých sa musíte obávať. Je tiež dôležité uvedomiť si nedostatok entropických zraniteľností, ktoré spôsobujú, že aplikácie sú zraniteľné voči hrubej sile citlivých tokenov, kľúčov alebo nonces, ktoré nie sú technicky kryptografické, ale používajú sa pri prevádzke netriviálnych funkcií aplikácií.

A teraz je všetko po starom

Teraz vieme, že používanie PRNG zabudovaných do PHP sa považuje za nedostatok zraniteľnosti v oblasti entropie (t. j. zníženie neistoty uľahčuje hrubú silu). Môžeme rozšíriť náš útok:


Zraniteľnosť pri sprístupnení informácií robí túto metódu generovania tokenov úplne zbytočnou. Aby sme pochopili prečo, pozrime sa bližšie na PHP funkciu uniqid() . Jeho definícia:


Na základe aktuálneho času v mikrosekundách získa jedinečný predponový identifikátor.


Ako si pamätáte, entropia je miera neistoty. Z dôvodu zraniteľnosti pri zverejňovaní informácií môžu hodnoty generované mt_rand() uniknúť, takže použitie mt_rand() ako jedinečného predponu identifikátora pridáva nulovú neistotu. V našom príklade je jediným ďalším druhom vstupu do uniqid() čas. Ale určite to NIE JE vágne. Mení sa lineárne a predvídateľne. A predvídateľné hodnoty majú extrémne nízku entropiu.


Samozrejme, definícia sa vzťahuje na "mikrosekundy", teda milióntiny sekundy. To nám dáva 1 000 000 možných čísel. Tu ignorujem hodnoty väčšie ako 1 sekunda, pretože ich zlomok a merateľnosť sú také veľké (napríklad hlavička HTTP Date v odpovedi), že nedáva takmer nič. Predtým, ako pôjdeme do detailov, rozoberieme funkciu uniqid() a pozrieme sa na jej kód C:


gettimeofday((struct timeval *) &tv, (struct timezone *) NULL); sec = (int) tv.tv_sec; usec = (int) (tv.tv_usec % 0x100000); /* usec môže mať maximálnu hodnotu 0xF423F, takže používame * usecs iba päť hexadecimálnych čísel. */ if (more_entropy) ( spprintf(&uniqid, 0, "%s%08x%05x%.8F", predpona, sec, usec, php_combined_lcg(TSRMLS_C) * 10); ) else ( spprintf(&uniqid, 0, "% s%08x%05x", predpona, sek, použiť); ) RETURN_STRING(uniqid, 0);

Ak to vyzerá príliš komplikovane, môžete všetko replikovať v starom dobrom PHP:


function unique_id($prefix = "", $more_entropy = false) ( list($usec, $sec) = explode(" ", microtime()); $usec *= 1000000; if(true === $more_entropy) ( return sprintf("%s%08x%05x%.8F", $prefix, $sec, $usec, lcg_value()*10); ) else ( return sprintf("%s%08x%05x", $prefix, $ sek, $usec); ))

Tento kód nám hovorí, že jednoduché volanie uniqid() bez parametrov nám vráti 13-znakový reťazec. Prvých 8 znakov je aktuálna časová pečiatka Unixu (v sekundách), vyjadrená v šestnástkovej sústave. Posledných 5 znakov sú ďalšie mikrosekundy v šestnástkovej sústave. Inými slovami, základná funkcia uniqid() poskytuje veľmi presné meranie systémového času, ktorý možno extrahovať z jednoduchého volania uniqid() pomocou kódu, ako je tento:


$id = uniqid(); $time = str_split($id, 8); $sec = hexdec("0x" . $čas); $usec = hexdec("0x" . $čas); echo "Seconds: ", $sec, PHP_EOL, "Microseconds: ", $usec, PHP_EOL;

Pozrite sa na kód C. Presný systémový čas nie je nikdy skrytý vo výstupe, bez ohľadu na parametre:


echo uniqid(), PHP_EOL; // 514ee7f81c4b8 echo uniqid("prefix-"), PHP_EOL; // prefix-514ee7f81c746 echo uniqid("prefix-", true), PHP_EOL; // predpona-514ee7f81c8993.39593322

Jedinečné identifikátory hrubou silou

Po zamyslení je zrejmé, že odhalenie akejkoľvek hodnoty uniqid() útočníkovi je ďalším príkladom potenciálnej zraniteľnosti pri prezradení informácií. To uniká veľmi presný systémový čas, ktorý možno použiť na predpovedanie vstupu pre následné volania uniqid(). To pomáha vyriešiť všetky dilemy, ktoré vznikajú pri pokuse o predpovedanie mikrosekúnd zúžením 1 000 000 možností na užší rozsah. Keďže tento únik mohol byť spomenutý neskôr, v našom príklade nie je technicky potrebný. Pozrime sa ešte raz na pôvodný kód tokenu uniqid():


$token = hash("sha512", uniqid(mt_rand()));

Z tohto príkladu môžeme vidieť, že vykonaním resetovacieho útoku na mt_rand() v kombinácii so zverejnením informácií z uniqid() vypočítame relatívne malú množinu hashov SHA512, čo sa môže ukázať ako resetovanie hesla alebo iné dôležité tokeny. Ak potrebujete úzky rozsah časových pečiatok bez toho, aby ste využili únik systémového času z uniqid() , potom poďme analyzovať odpovede servera, ktoré zvyčajne obsahujú hlavičku HTTP Date. Odtiaľ môžete získať presné časové pečiatky servera. A keďže v tomto prípade sa entropia rovná miliónu možných hodnôt mikrosekúnd, môžete ju brutálne vynútiť za pár sekúnd!


Zachráni nás rastúca entropia?

Samozrejme, je možné pridať entropiu do uniqid() nastavením druhého parametra funkcie na TRUE:


Ako ukazuje kód C, nový zdroj entropie používa výstup internej funkcie php_combined_lcg(). Táto funkcia je vystavená užívateľskému priestoru prostredníctvom funkcie lcg_value(), ktorú som použil pri mojej PHP konverzii funkcie uniqid(). V podstate kombinuje dve hodnoty generované dvoma lineárnymi kongruentnými generátormi s oddelenými počiatočnými hodnotami. Nižšie je uvedený kód, ktorý dodal generátorom tieto počiatočné hodnoty. Rovnako ako v prípade mt_rand() sa generujú raz za proces PHP a opätovne sa používajú vo všetkých nasledujúcich volaniach.


static void lcg_seed(TSRMLS_D) /* ((( */ ( struct timeval tv; if (gettimeofday(&tv, NULL) == 0)) ( LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11); } else { LCG(s1) = 1; } #ifdef ZTS LCG(s2) = (long) tsrm_thread_id(); #else LCG(s2) = (long) getpid(); #endif /* Add entropy to s2 by calling gettimeofday() again */ if (gettimeofday(&tv, NULL) == 0) { LCG(s2) ^= (tv.tv_usec<<11); } LCG(seeded) = 1; }

Ak sa na to pozeráte príliš dlho a chcete niečo hodiť na monitor, je lepšie to nerobiť. Monitory sú v dnešnej dobe drahé.


Obe počiatočné hodnoty používajú funkciu C gettimeofday() na zachytenie aktuálneho času v sekundách a mikrosekundách od epochy Unixu (s odkazom na hodiny servera). Treba si uvedomiť, že obe volania sú implementované v zdrojovom kóde, takže hodnota počítadla microsecond() medzi nimi bude minimálna, čo znižuje zavedenú neistotu. Druhá počiatočná hodnota bude tiež primiešaná do ID aktuálneho procesu, ktoré vo väčšine prípadov pod Linuxom nepresiahne 32 768. Samozrejme, limit môžete manuálne zvýšiť na približne 4 milióny zmenou /proc/sys/kernel/pid_max , ale to je veľmi nežiaduce.


Ukazuje sa, že primárnym zdrojom entropie využívaným týmito LCG sú mikrosekundy. Pamätáte si napríklad našu počiatočnú hodnotu mt_rand()? Hádajte, ako sa to počíta.


#ifdef PHP_WIN32 #define GENERATE_SEED() (((dlhý) (čas(0) * GetCurrentProcessId())) ^ ((dlhý) (1000000.0 * php_combined_lcg(TSRMLS_C)))) #else #define GENERATE_SEED() (((long) ) (čas(0) * getpid())) ^ ((dlhý) (1000000,0 * php_combined_lcg(TSRMLS_C)))) #endif

To znamená, že všetky počiatočné hodnoty používané v PHP sú vzájomne závislé. Aj tie isté vstupné údaje sa niekoľkokrát zmiešajú. Možno by ste mohli obmedziť rozsah počiatočných mikrosekúnd, ako sme diskutovali vyššie: pomocou dvoch dotazov, pričom prvý bude preskakovať medzi sekundami (takže mikročas by bol 0 + čas vykonania nasledujúceho volania gettimeofday() C). Môžete dokonca vypočítať mikrosekundovú deltu medzi ostatnými volaniami gettimeofday(), ak máte prístup k zdrojovému kódu (pomáha to open source charakter PHP). Nehovoriac o tom, že hrubé vynútenie počiatočnej hodnoty mt_rand() vám dáva konečnú počiatočnú hodnotu, ktorá umožňuje offline overenie.


Hlavný problém však spočíva v php_combined_lcg() . Toto je nízkoúrovňová implementácia funkcie lcg_value() v užívateľskom priestore, ktorá získa počiatočnú hodnotu raz za proces PHP. A ak poznáte túto hodnotu, môžete predpovedať výstup. Ak rozlúsknete tento oriešok, je to, hra sa skončila.

Existuje na to aplikácia...

Veľa času som venoval praktickým veciam, tak sa k nim vráťme ešte raz. Nie je také ľahké získať dve počiatočné hodnoty, ktoré používa php_combined_lcg() - nemusí byť možné vytvoriť priamy únik. Funkcia lcg_value() je pomerne málo známa a programátori sa častejšie spoliehajú na mt_rand(), keď potrebujú PRNG zabudované v PHP. Nechcem zabrániť lcg_value() v úniku hodnoty, ale je to nepopulárna funkcia. Dvojica kombinovaných LCG tiež neodráža funkciu seedingu (takže nemôžete len hľadať volania mt_srand() na identifikáciu netesného mechanizmu seedovania zdedeného z cudzieho kódu). Existuje však spoľahlivý zdroj, ktorý priamo poskytuje údaje pre semená hrubej sily: ID relácie v PHP.


spprintf(&buf, 0, "%.15s%ld%ld%0.8F", remote_addr ? remote_addr: "", tv.tv_sec, (dlhá int)tv.tv_usec, php_combined_lcg(TSRMLS_C) * 10);

Tento kód generuje pre-hash hodnotu pre ID relácie pomocou IP, časovej značky, mikrosekúnd a... php_combined_lcg() výstupu. Vzhľadom na výrazné zníženie počtu mikro-časových možností (tento kód potrebuje 1 na vygenerovanie ID a 2 na php_combined_lcg() , čo vedie k minimálnemu rozdielu medzi nimi), môžeme to teraz hrubou silou. No, pravdepodobne.


Ako si možno pamätáte, PHP teraz podporuje nové možnosti relácie ako session.entropy_file a session.entropy_length. Toto sa robí, aby sa predišlo ID relácií hrubou silou, počas ktorých môžete rýchlo (netrvá to hodiny) získať dve počiatočné hodnoty pre generátory LCG kombinované pomocou php_combined_lcg(). Ak používate PHP 5.3 alebo nižšie, možno ste tieto možnosti nakonfigurovali nesprávne. To znamená, že existuje ďalšia užitočná chyba zabezpečenia pri zverejňovaní informácií, ktorá vám umožňuje použiť ID relácie hrubou silou, aby ste získali počiatočné hodnoty pre LCG.


Pre takéto prípady existuje aplikácia pre Windows, ktorá umožňuje vypočítať hodnoty LCG.


Mimochodom, znalosť stavov LCG vám umožňuje pochopiť, ako mt_rand() získa počiatočnú hodnotu, takže toto je ďalší spôsob, ako obísť nedostatok únikov hodnôt mt_rand().


Čo to všetko znamená z hľadiska pridania entropie k návratovým hodnotám uniqid()?


$token = hash("sha512", uniqid(mt_rand(), true));

Toto je ďalší príklad potenciálnej zraniteľnosti nedostatku entropie. Pri únikoch sa nemôžete spoliehať na entropiu (aj keď za ne nezodpovedáte!). Vďaka úniku informácií o ID relácie môže útočník predpovedať aj hodnotu entropie, ktorá bola k tomuto ID dodatočne pridaná.


Opäť, kto je na vine? Ak sa aplikácia X spolieha na uniqid() , ale používateľ alebo iná aplikácia na tom istom serveri unikne internému stavu LCG, potom musíte konať v oboch situáciách. Používatelia by si mali byť istí, že ID relácie používajú dostatočne vysokú entropiu a programátori tretích strán by mali pochopiť, že ich metódam generovania náhodných hodnôt chýba entropia, takže je potrebné prejsť na vhodnejšie alternatívy (aj keď sú dostupné len slabé zdroje entropie!) .

Pri hľadaní entropie

PHP samo o sebe nie je schopné generovať silnú entropiu. Neexistuje ani základné API na prenos údajov z generátorov PRNG na úrovni operačného systému, ktoré sú spoľahlivými zdrojmi silnej entropie. Preto sa musíte spoľahnúť na voliteľné rozšírenia openssl a mcrypt. Ponúkajú funkcie, ktoré sú oveľa lepšie ako ich deravé, predvídateľné, nízkoentropické príbuzné.


Žiaľ, keďže obe rozšírenia sú voliteľné, v niektorých prípadoch nemáme inú možnosť, ako sa v krajnom prípade spoliehať na slabé zdroje entropie. Keď sa to stane, slabá entropia mt_rand() musí byť doplnená o ďalšie zdroje neistoty, zmiešaním ich údajov do jedného fondu, z ktorého je možné čerpať pseudonáhodné bajty. Podobný náhodný generátor využívajúci silný zmiešavač entropie už implementoval Anthony Ferrara vo svojej knižnici RandomLib. Toto by mali programátori robiť vždy, keď je to možné.


Vyhnite sa pokušeniu skryť slabosť svojej entropie hashovaním zložitých matematických transformácií. Útočník toto všetko zopakuje hneď, ako zistí primárnu počiatočnú hodnotu. Takéto triky len mierne zvýšia množstvo výpočtov počas hrubej sily. Nezabudnite: čím nižšia entropia, tým menšia neistota; Čím menej neistoty je, tým menej príležitostí je potrebné hrubo vynútiť. Jediným ospravedlniteľným riešením je zvýšiť zásobu entropie, ktorú používate, akýmikoľvek dostupnými prostriedkami.


Knižnica RandomLib generuje náhodné bajty zmiešaním údajov z rôznych zdrojov entropie a lokalizáciou informácií, ktoré môže útočník potrebovať na uhádnutie. Môžete napríklad zmiešať výstupy mt_rand(), uniqid() a lcg_value(), pridať PID, spotrebu pamäte, nejaké ďalšie mikročasové meranie, serializáciu $_ENV, posix_times() atď. Alebo ísť ešte ďalej, to umožňuje RandomLib rozšíriteľnosť. Povedzme, že používame nejaké delty v mikrosekundách (t. j. meriame, koľko mikrosekúnd potrebuje funkcia na prácu s pseudonáhodnými vstupnými údajmi, ako sú volania hash()).

Pridať značky

rand(1,N), ale s výnimkou poľa(a,b,c,..)

existuje už vstavaná funkcia, ktorú nepoznám alebo ju musím implementovať sám (ako?)?

AKTUALIZOVAŤ

Kvalifikované riešenie by malo mať zlato bez ohľadu na to, aká veľká alebo malá je veľkosť vylúčeného poľa.

Nie je tam vstavaná funkcia, ale vy môžeš urob to:

Funkcia randWithout($from, $to, pole $výnimky) ( sort($exceptions); // nám umožňuje použiť break; vo foreach spoľahlivo $number = rand($from, $to - count($exceptions)); / / alebo mt_rand() foreach ($exceptions as $exception) ( if ($number >= $exception) ( $number++; // vykompenzovať medzeru ) else /*if ($number< $exception)*/ { break; } } return $number; }

Nie je to v mojej hlave, takže by sa to dalo leštiť – ale aspoň sa nemôžete dostať do scenára nekonečnej slučky, ani len hypoteticky.

Poznámka: Funkcia sa preruší, ak $výnimky výfuky váš rozsah - napríklad volanie randWithout(1, 2, array(1,2)) alebo randWithout(1, 2, array(0,1,2,3)) neprinesie nič rozumné (samozrejme), ale v tomto prípade vrátené číslo bude mimo rozsahu $od – $do, takže je ľahké ho zachytiť.

Ak je zaručené, že $výnimky sú už zoradené, sort($exceptions); môžu byť vymazané.

Očná pastva: Trochu minimálna vizualizácia algoritmu.

Nemyslím si, že existuje taká vstavaná funkcia; pravdepodobne si to budete musieť naprogramovať sami.

Na kódovanie máte dve riešenia:

  • Použite cyklus na volanie rand() alebo mt_rand(), kým nevráti správnu hodnotu
    • čo znamená volanie rand() viackrát, v najhoršom prípade
    • ale malo by to fungovať, ak je N veľké a nemáte veľa zakázaných hodnôt.
  • Vytvorte pole obsahujúce iba zákonné hodnoty
    • A používať pole_rand aby ste z neho vybrali jednu hodnotu
    • čo bude fungovať dobre, ak je N malé

V závislosti od toho, čo potrebujete a prečo, môže byť tento prístup zaujímavou alternatívou.

$cisla = pole_rozdiel(rozsah(1, N), pole(a, b, c)); // Buď (nie je to skutočná odpoveď, ale môže byť užitočná, v závislosti od vašich okolností) shuffle($numbers); // $numbers je teraz náhodne zoradené pole obsahujúce všetky čísla, ktoré vás zaujímajú // Alebo: $x = $numbers; // $x je teraz náhodné číslo vybrané zo skupiny čísel, ktoré vás zaujímajú

Ak teda nepotrebujete zakaždým generovať množinu potenciálnych čísel, ale generovať množinu raz a potom vybrať z tej istej množiny náhodných čísel, môže to byť dobrý spôsob.

Najjednoduchší spôsob…

Musíte vypočítať pole chýbajúcich miest, aby ste mohli vybrať náhodnú pozíciu v súvislom poli dĺžky M = N - počet výnimiek a jednoducho ju namapovať späť na pôvodné pole s dierami. To bude vyžadovať čas a priestor rovný odovzdanému poľu. Neviem php z diery v zemi, tak pardon za semi-psudo textový príklad kódu.

  1. Vytvorte nové pole Offset rovnakej dĺžky ako pole Exceptions.
  2. v Offset[i] uloží prvý index do imaginárneho neprázdneho poľa, ktorému by v pôvodnom poli chýbali prvky i.
  3. Teraz vyberte náhodný prvok. Vyberte náhodné číslo, r , na 0..M počet zostávajúcich prvkov.
  4. Nájsť ten posun[i]<= r < Offest это легко с бинарным поиском
  5. Návrat r+i

Toto je len náčrt, budete sa musieť vysporiadať s koncami polí a ak je niečo indexované vo forme 0 alebo 1 a všetok ten jazz. Ak ste chytrí, môžete skutočne vypočítať pole Offset za chodu z originálu, ale to je trochu menej jasné.

Možno príliš neskoro na odpoveď, ale našiel som tento kúsok kódu niekde v mojej mysli, keď som sa snažil získať náhodné údaje z databázy na základe náhodného ID okrem nejakého čísla.

$excludedData = pole(); // Toto je vaše vylúčené číslo $maxVal = $this->db->count_all_results("game_pertanyaan"); // Získanie maximálneho počtu na základe mojej databázy $randomNum = rand(1, $maxVal); // Urobte prvú iniciáciu, myslím, že to môžete vložiť priamo do parametra while > in_array, zdá sa, že tiež funguje, je to na vás, zatiaľ čo (in_array($randomNum, $excludedData)) ( $randomNum = rand(1, $maxVal); ) $randomNum; //Vaše náhodné číslo s výnimkou čísla, ktoré si vyberiete

Už sa ma párkrát pýtali, ako sa mám náhodný výstup úvodzoviek na mojej webovej stránke v bloku " Inteligentné ponukyĎalej sa mi podarilo zistiť, že problém je v nepochopení ľudí, ako získať náhodný prvok z poľa v PHP. Úloha je jednoduchá, no napriek tomu, keďže vznikajú otázky, je potrebné na ne odpovedať.

Hneď vám dám kód. Povedzme, že existuje pole so sadou úvodzoviek. A musíte vybrať jeden náhodný z nich a výstup:

$úvodzovky = pole(); // Inicializuje prázdne pole
$quotes = "Buďte pozorní na svoje myšlienky, sú začiatkom činov."; // Prvý citát
$quotes = "Nie tí najmúdrejší alebo najsilnejší prežijú, ale tí, ktorí sú najviac vnímaví k zmenám."; // Druhý citát
$quotes = "Život je hora: pomaly stúpaš, rýchlo klesáš."; // Tretí citát
$quotes = "Ľudia nechcú byť bohatí, ľudia chcú byť bohatší ako ostatní."; // Štvrtý citát
$číslo = mt_rand(0, počet($úvodzovky) - 1); // Vezmite náhodné číslo od 0 do (dĺžka poľa mínus 1) vrátane
echo $citáty[$číslo]; // Výstup cenovej ponuky
?>

Kľúčovým bodom je získanie náhodného čísla. Stačí si nastaviť správne hranice. Ak potrebujete vybrať náhodný prvok po celej dĺžke poľa, potom je to z 0 predtým ( dĺžka poľa mínus 1). A potom už len vytiahnuť prvok z poľa s výsledným náhodným indexom.

Pokiaľ ide o úlohu s úvodzovkami, je lepšie ich uložiť do databázy. V zásade, ak je stránka veľmi jednoduchá, dá sa to urobiť v textovom súbore. Ale ak v databáze, potom je lepšie použiť RAND() A LIMIT V SQL dotaz, takže okamžite dostanete jednu a náhodnú cenovú ponuku z databázy.

  • Od:
  • Registrovaný: 2014.07.07
  • Príspevky: 3,775
  • Mám rád PunBB:
  • 5 roky, 6 mesiace,
  • Páči sa: 463

Téma: Ako vybrať náhodnú hodnotu z poľa v PHP

Pomocou tejto funkcie môžeme vybrať náhodný prvok (alebo prvky) poľa. Áno, presne ten prvok alebo prvky! Môže to byť jeden prvok alebo ich môže byť niekoľko. Všetko závisí od úlohy, ktorej čelíte.

Tu však treba brať do úvahy, že funkcia nevráti hodnotu prvku, ale jeho kľúča (alebo kľúčov, ak je prvkov viacero).

Funkcia berie ako parametre v zátvorkách: názov poľa, s ktorým pracujeme, a počet prvkov, ktoré je potrebné vybrať.

Vo všeobecnosti je všetko jednoduché! A bude to ešte jednoduchšie, keď sa na to všetko pozrieme na príkladoch.

Najprv vyberieme jeden náhodný prvok z poľa.

2 Odpovedať od PunBB

  • Od: Moskva, Sovchoznay 3, apt. 98
  • Registrovaný: 2014.07.07
  • Príspevky: 3,775
  • Mám rád PunBB:
  • 5 roky, 6 mesiace,
  • Páči sa: 463

Predstavme si, že niekde v hornej časti našej webovej stránky chceme zobraziť nejaké citáty. Samozrejme, citáty sa musia zmeniť. Vždy, keď používateľ navštívi vašu stránku, chcete, aby používateľ videl novú cenovú ponuku.

Ako ste pravdepodobne uhádli, najjednoduchší spôsob, ako to implementovať, je umiestniť všetky dostupné úvodzovky a výroky do poľa a potom vybrať náhodný prvok z tohto poľa a zobraziť ho na obrazovke.

Čím viac úvodzoviek máte v poli, tým je menšia pravdepodobnosť, že sa budú opakovať.

Ale kvôli príkladu sa nebudem príliš obťažovať a vložím do svojho poľa 7 výrokov.

Ďalej si budem musieť vytvoriť premennú, do ktorej uložím výsledok funkcie array_rand(). V zátvorkách bude mať táto funkcia dva argumenty: názov nášho poľa a počet náhodných prvkov, ktoré potrebujeme.

Ako som už povedal, funkcia nevracia hodnotu prvku, ale jeho kľúč (alebo číslo v zozname). Kľúč náhodného prvku bude teda uložený v premennej.

Potom už len potrebujem zobraziť hodnotu požadovaného prvku. Na tento účel uvediem názov poľa a v hranatých zátvorkách názov našej premennej, ktorá obsahuje náhodný kľúč.

To je všetko. Pozrite sa na kód nižšie a myslím, že všetko úplne pochopíte:

". $frases[$rand_frases] .""; ?>

3 Odpovedať od PunBB

  • Od: Moskva, Sovchoznay 3, apt. 98
  • Registrovaný: 2014.07.07
  • Príspevky: 3,775
  • Mám rád PunBB:
  • 5 roky, 6 mesiace,
  • Páči sa: 463

Re: Ako vybrať náhodnú hodnotu z poľa v PHP

Teraz si precvičme tlač niekoľkých náhodných prvkov poľa.

V prípade jedného prvku sa vráti jeho kľúč a v prípade viacerých prvkov náhodného poľa sa vráti pole kľúčov. Z toho budeme pri zobrazovaní na obrazovke vychádzať.

Najprv si vytvoríme pole, do ktorého pridáme 7 rôznych mien.

Ďalej vytvoríme premennú, do ktorej bude zaznamenaná práca funkcie array_rand(). Až teraz v zátvorkách pre túto funkciu uvádzame číslo „2“ ako druhý argument. To znamená, že potrebujeme 2 náhodné prvky.

Výsledkom funkcie v tejto situácii bude pole obsahujúce dva náhodné kľúče prvkov z nášho hlavného poľa.

Preto pri zobrazovaní na obrazovke treba s tým počítať a v hranatých zátvorkách uviesť nielen názov premennej, ale aj názov premennej, potom hranaté zátvorky a index poľa. Keďže máme 2 prvky, v prvom prípade bude index , a v druhom . (Pamätáš si, že indexovanie v poliach začína na "0".)

To je všetko. Pozrite sa na kód, aby bolo všetko jasnejšie:

$names = array("Masha", "Sasha", "Naďa", "Mila", "Andrey", "Sergey", "Anton"); $rand_names = array_rand($names,2); ozvena"

".$names[$rand_names]." a ".$names[$rand_names]."

";

V dôsledku toho sa na obrazovke zobrazia dve náhodné mená. Pri každom obnovení stránky sa názvy zmenia.

Zdroj

Náhodné čísla sú neoddeliteľnou súčasťou programovania, najmä pokiaľ ide o bezpečnostné systémy. Napríklad kryptografia sa spolieha na generovanie náhodných hodnôt, aby produkovala čísla, ktoré nemožno predvídať. Samozrejme, v PHP hrajú náhodné čísla obrovskú úlohu: pomocou nich môžeme generovať tokeny, soli a ďalšie hodnoty.

Generovanie náhodných čísel je založené na špeciálnych algoritmoch. Vstupným parametrom, od ktorého sa algoritmus spustí, môže byť náhodná hodnota alebo vopred určená hodnota.

V tomto článku budeme hovoriť o náhodných číslach: ako sa generujú a kde sa dajú použiť.

Používanie náhodných čísel

V PHP hrajú náhodné čísla obrovskú úlohu, pretože... veľmi často používané na rôzne účely. V podstate súvisia s bezpečnosťou. Na ich základe sa generujú CSRF tokeny, API kľúče, autentifikačné hodnoty, hodnoty pre resetovanie hesla a mnohé ďalšie. To všetko sa deje tak, že výslednú hodnotu nie je možné predvídať.

Najdôležitejšie príklady použitia náhodných hodnôt sú:

  • Generovanie soli pre kryptografiu- náhodné číslo soli sa spravidla používa na jednosmerné šifrovanie, ako aj na hashovanie hesiel. Táto náhodná hodnota sa používa ako inicializačný vektor v kryptografii.
  • Generujte náhodné hodnoty, ako napríklad ID relácie- PHP sa používa na vytváranie obrovského množstva aplikácií, kde je bezpečnosť na prvom mieste. Veľa funkcií je založených na práci s reláciami a vygenerovanými ID relácií.
  • Generovanie autentifikačných tokenov, ktoré je takmer nemožné predvídať- mnohé PHP aplikácie sú založené na spolupráci s inými systémami cez špeciálne API rozhrania. Pred použitím rozhrania API musíte zvyčajne prejsť procesom autentifikácie. Získanie ťažko dostupných hodnôt pre tokeny je veľmi ťažké. Preto sa v týchto úlohách používajú náhodné čísla.

Generátory náhodných čísel

Náhodné čísla použité vo vyššie opísaných prípadoch sú generované pseudogenerátormi v PHP. K dispozícii je niekoľko algoritmov:

    Lineárna kongruentná metóda pri použití funkcie lcg_value().

    Mersennov vír, ktorý používa funkcia mt_rand().

    Funkcia rand() používa podobnú funkciu v jazyku C.

V skutočnosti tieto funkcie nevracajú náhodné čísla, ale čísla distribuované takým spôsobom, že sa javia ako náhodné. Poradie týchto čísel závisí od základného náhodného čísla v rámci implementovaného algoritmu.

Základné čísla pre generátory

Základné čísla alebo vektory základných čísel sú súbory údajov, ktoré sa používajú na generovanie náhodných čísel. Generátory pseudonáhodných čísel fungujú len tak, že začínajú od nich. Ak útočník pozná toto základné číslo, v budúcnosti bude môcť predpovedať hodnoty vašich náhodných čísel.

V PHP môžete zadať základné čísla dvoma spôsobmi. Prvým je použitie funkcie mt_srand(). Táto metóda sa používa najmä pri jednotkových testoch náhodnej série. Druhý spôsob je nechať PHP generovať základné čísla samo. Od verzie 4.2 túto funkciu poskytuje PHP. Následne bude Mersennov Vortex použitý na generovanie náhodných čísel.

PHP generuje základné číslo v závislosti od operačného systému. Na platformách Linux môžete použiť funkcie mcrypt_create_iv() alebo openssl_pseudo_random_bytes() v /dev/urandom. Windows poskytuje špeciálny pseudogenerátor, ku ktorému je možné pristupovať prostredníctvom funkcií openssl_pseudo_random_bytes() a mcrypt_create_iv().

Spodná čiara

Keďže náhodné čísla hrajú obrovskú úlohu pri vytváraní bezpečných webových aplikácií, musíme vedieť viac o tom, ako fungujú. Ak chcete generovať základné čísla pre pseudogenerátory sami, uistite sa, že vaše metódy sú spoľahlivé.