• Php náhodná hodnota. Pár slov o generování náhodných čísel v PHP. Použití náhodných čísel

    rand(1,N), ale bez pole(a,b,c,..)

    existuje již vestavěná funkce, kterou neznám nebo ji musím implementovat sám (jak?)?

    AKTUALIZACE

    Kvalifikované řešení by mělo mít zlato, bez ohledu na to, jak velká nebo malá je velikost vyloučeného pole.

    Není tam žádná vestavěná funkce, ale ty můžeš Udělej to:

    Funkce randWithout($from, $to, pole $výjimky) ( sort($exceptions); // nám umožňuje použít break; ve foreach spolehlivě $number = rand($from, $to - count($exceptions)); / / nebo mt_rand() foreach ($exceptions as $exception) ( if ($number >= $exception) ( $number++; // vyrovnat mezeru ) else /*if ($number< $exception)*/ { break; } } return $number; }

    Není to v mé hlavě, takže by se to mohlo hodit leštění - ale alespoň nemůžete skončit ve scénáři nekonečné smyčky, a to ani hypoteticky.

    Poznámka: Funkce se přeruší, pokud $výjimky výfuky váš rozsah - například volání randWithout(1, 2, array(1,2)) nebo randWithout(1, 2, array(0,1,2,3)) nepřinese nic rozumného (samozřejmě), ale v tomto případě vrácené číslo bude mimo rozsah $od – $do, takže je snadné jej zachytit.

    Pokud je zaručeno, že $výjimky jsou již setříděny, sort($exceptions); mohou být odstraněny.

    Pastvou pro oči: Poněkud minimální vizualizace algoritmu.

    Myslím, že taková vestavěná funkce tam není; pravděpodobně si to budete muset naprogramovat sami.

    Chcete-li to kódovat, máte dvě řešení:

    • Použijte smyčku k volání rand() nebo mt_rand(), dokud nevrátí správnou hodnotu
      • což znamená volání rand() několikrát, nejhorší scénář
      • ale to by mělo fungovat dobře, pokud je N velké a nemáte mnoho zakázaných hodnot.
    • Vytvořte pole obsahující pouze zákonné hodnoty
      • A používat pole_rand vyberte z něj jednu hodnotu
      • což bude fungovat dobře, pokud je N malé

    V závislosti na tom, co potřebujete a proč, může být tento přístup zajímavou alternativou.

    $čísla = pole_diff(rozsah(1, N), pole(a, b, c)); // Buď (není to skutečná odpověď, ale mohla by být užitečná, v závislosti na vašich okolnostech) shuffle($numbers); // $numbers je nyní náhodně seřazené pole obsahující všechna čísla, která vás zajímají // Nebo: $x = $numbers; // $x je nyní náhodné číslo vybrané ze sady čísel, která vás zajímají

    Pokud tedy nepotřebujete pokaždé generovat sadu potenciálních čísel, ale vygenerujete sadu jednou a poté vyberete spoustu náhodných čísel ze stejné sady, může to být dobrý způsob.

    Nejjednodušší způsob…

    Musíte vypočítat pole chybějících míst, abyste mohli vybrat náhodnou pozici v souvislém poli délky M = N - počet výjimek a snadno ji namapovat zpět na původní pole s dírami. To bude vyžadovat čas a prostor rovnající se předávanému poli. Neznám php z díry v zemi, takže omluvte ukázkový kód semi-psudo textu.

    1. Vytvořte nové pole Offset stejné délky jako pole Exceptions.
    2. v Offset[i] uloží první index do imaginárního neprázdného pole, ve kterém by chyběly prvky i v původním poli.
    3. Nyní vyberte náhodný prvek. Vyberte náhodné číslo, r , na 0..M počet zbývajících prvků.
    4. Najít i ten posun[i]<= r < Offest это легко с бинарным поиском
    5. Návrat r+i

    Nyní je to jen náčrt, budete se muset vypořádat s konci polí a pokud je něco indexováno ve formě 0 nebo 1 a všechen ten jazz. Pokud jste chytří, můžete skutečně vypočítat pole Offset za chodu z originálu, ale to je trochu méně jasné.

    Možná příliš pozdě na odpověď, ale našel jsem tento kus kódu někde v mé mysli, když jsem se snažil získat náhodná data z databáze na základě náhodného ID kromě nějakého čísla.

    $excludedData = pole(); // Toto je vaše vyloučené číslo $maxVal = $this->db->count_all_results("game_pertanyaan"); // Získání maximálního počtu na základě mé databáze $randomNum = rand(1, $maxVal); // Proveďte první iniciaci, myslím, že to můžete vložit přímo do parametru while > in_array, zdá se, že také funguje, je to na vás while (in_array($randomNum, $excludedData)) ( $randomNum = rand(1, $maxVal); ) $randomNum; //Vaše náhodné číslo kromě čísla, které si vyberete

    vybrat prvek (11)

    Mám pole s názvem $ran = array(1,2,3,4);

    Potřebuji získat náhodnou hodnotu z tohoto pole a uložit ji do proměnné, jak to mohu udělat?

    Odpovědi

    Svou odpověď zakládám na funkci @OlafurWaage. Pokusil jsem se to použít, ale při pokusu o změnu vráceného objektu jsem narazil na problémy s nápovědou. Aktualizoval jsem jeho funkci, aby předala a vrátila se odkazem. Nová vlastnost:

    Funkce &random_value(&$array, $default=null) ( $k = mt_rand(0, count($array) - 1); if (isset($array[$k])) ( return $array[$k]; ) else ( return $default; ) )

    Další informace najdete v mé otázce v části Předávání/vracení referencí na objekt + Změna objektu nefunguje

    $hodnota = $pole;

    K výběru náhodného klíče z pole můžete použít funkci array_rand, jak je ukázáno níže.

    $array = array("jeden", "dva", "tři", "čtyři", "pět", "šest"); echo $array;

    nebo můžete použít funkce rand a count pro výběr náhodného indexu.

    $array = array("jeden", "dva", "tři", "čtyři", "pět", "šest"); echo $array;

    $náhodný = $běžel;

    To je také užitečné jako funkce, pokud potřebujete hodnotu

    Funkce random_value($array, $default=null) ( $k = mt_rand(0, count($array) - 1); return isset($array[$k])? $array[$k]: $default; )

    Má vaše volba nějaké bezpečnostní důsledky? Pokud ano, použijte random_int() a array_keys() . (random_bytes() je pouze PHP 7, ale pro PHP 5 existuje polyfill).

    Funkce random_index(pole $zdroj) ( $max = počet($zdroj) - 1; $r = random_int(0, $max); $k = klíče_pole($zdroj); return $k[$r]; )

    Aplikace:

    $array = [ "jablko" => 1234, "chlapec" => 2345, "kočka" => 3456, "pes" => 4567, "echo" => 5678, "štěstí" => 6789 ]; $i = random_index($array); var_dump([$i, $array[$i]]);

    Funkce array_random($array, $amount = 1) ( $keys = array_rand($array, $amount); if ($amount == 1) ( return $array[$keys]; ) $results = ; foreach ($keys jako $key) ( $results = $array[$key]; ) vrátit $results; )

    Aplikace:

    $items = ["foo", "bar", "baz", "lorem"=>"ipsum"]; pole_random($položky); // "bar" array_random($items, 2); // ["foo", "ipsum"]

    Pár poznámek:

    • $částka musí být menší nebo rovna count($array) .
    • array_rand() nezamíchá klíče (od PHP 5.2.10, viz), takže prvky, které vyberete, budou vždy v původním pořadí. V případě potřeby použijte shuffle().

    Upravit: Funkce Laravel se od té doby značně rozrostla, viz Laravel 5,4"s Arr::random() . Zde je něco složitějšího, co vychází z vyspělé funkce Laravel:

    Funkce array_random($array, $number = null) ( $requested = ($number === null) ? 1: $number; $count = count($array); if ($requested > $count) (hodit nový \ RangeException("Požadovali jste ($požadované) položky, ale k dispozici jsou pouze ($count) položky."); ) if ($number === null) ( return $array; ) if ((int) $number == = 0) ( return ; ) $keys = (pole) array_rand($pole, $číslo); $results = ; foreach ($klíče jako $klíč) ( $results = $pole[$klíč]; ) return $results; )

    Několik zajímavostí:

    • Vyhoďte výjimku, pokud není dostatek dostupných položek
    • array_random($array, 1) vrátí pole jednoho prvku (#19826)
    • Podporovaná hodnota "0" pro počet prvků (To je jen čisté a jednoduché.

    Náhodné hodnoty jsou v PHP všude. Ve všech rámcích, v mnoha knihovnách. Pravděpodobně jste sami napsali spoustu kódu, který používá náhodné hodnoty ke generování tokenů a solí a jako vstup pro funkce. Také náhodné hodnoty hrají důležitou roli při řešení různých problémů:

    1. Náhodný výběr možností ze skupiny nebo řady známých možností.
    2. Generování inicializačních vektorů pro šifrování.
    3. Pro generování nepředvídatelných tokenů nebo jednorázových hodnot během autorizace.
    4. Ke generování jedinečných identifikátorů, jako jsou ID relace.

    Ve všech těchto případech existuje charakteristická zranitelnost. Pokud útočník uhodne nebo předpoví výstup vašeho generátoru náhodných čísel (RNG) nebo generátoru pseudonáhodných čísel (PRNG), bude schopen vypočítat tokeny, soli, nonce a kryptografické inicializační vektory vytvořené tímto generátorem. Proto je velmi důležité generovat kvalitní náhodné hodnoty, tedy takové, které je extrémně obtížné předvídat. Nikdy nedávejte tokeny pro resetování hesla, tokeny CSRF, klíče API, nonces nebo autorizační tokeny předvídatelné!


    Existují dvě další potenciální zranitelnosti spojené s náhodnými hodnotami v PHP:

    1. Zveřejňování informací.
    2. Nedostatečná entropie.

    V této souvislosti se „zveřejněním informací“ rozumí únik vnitřního stavu generátoru pseudonáhodných čísel – jeho počáteční hodnoty. Takové úniky mohou výrazně usnadnit předpovídání budoucích výstupů PRNG.


    „Nedostatek entropie“ popisuje situaci, kdy je variabilita počátečního vnitřního stavu (seed) PRNG nebo jeho výstupu tak malá, že celý rozsah možných hodnot lze poměrně snadno hrubou silou. Pro PHP programátory to není příliš dobrá zpráva.


    Na obě zranitelnosti se podíváme podrobně s ukázkovými scénáři útoku. Nejprve si ale ujasněme, co to vlastně náhodná hodnota je, pokud jde o programování v PHP.

    Co dělají náhodné hodnoty?

    Zmatek ohledně účelu náhodných proměnných je umocněn obecným nedorozuměním. Nepochybně jste slyšeli o rozdílu mezi kryptograficky silnými náhodnými hodnotami a vágními „unikátními“ hodnotami „pro jiné použití“. Hlavním dojmem je, že náhodné hodnoty používané v kryptografii vyžadují vysoce kvalitní náhodnost (nebo přesněji vysokou entropii), zatímco hodnoty pro jiné aplikace si vystačí s menší entropií. Tento dojem považuji za falešný a kontraproduktivní. Skutečný rozdíl mezi nepředvídatelnými náhodnými hodnotami a hodnotami potřebnými pro triviální úkoly spočívá v tom, že jejich předvídatelnost nemá škodlivé důsledky. To vylučuje kryptografii z úvahy úplně. Jinými slovy, pokud použijete náhodnou hodnotu v netriviálním problému, měli byste automaticky zvolit mnohem silnější RNG.


    Síla náhodných hodnot je určena entropií vynaloženou na jejich generování. Entropie je míra nejistoty vyjádřená v „bitech“. Například, když vezmu binární bit, jeho hodnota může být 0 nebo 1. Pokud útočník nezná přesnou hodnotu, pak máme entropii 2 bitů (tedy hod mincí). Pokud útočník ví, že hodnota je vždy 1, pak máme entropii 0 bitů, protože předvídatelnost je antonymem nejistoty. Také počet bitů se může pohybovat od 0 do 2. Pokud je například 99 % případů binární bit 1, pak může být entropie o něco větší než 0. Takže čím více nedefinovaných binárních bitů vybereme, tím lépe.


    V PHP je to vidět jasněji. Funkce mt_rand() generuje náhodné hodnoty, vždy se jedná o čísla. Nezobrazuje písmena, speciální znaky ani jiné významy. To znamená, že na každý bajt má útočník mnohem méně odhadů, tj. nízkou entropii. Pokud nahradíme mt_rand() čtením bajtů z linuxového zdroje /dev/random , dostaneme skutečně náhodné bajty: jsou generovány na základě šumu generovaného ovladači systémových zařízení a dalšími zdroji. Je zřejmé, že tato možnost je mnohem lepší, protože poskytuje podstatně více bitů entropie.


    Nežádoucnost mt_rand() naznačuje i to, že se nejedná o generátor skutečně náhodných, ale pseudonáhodných čísel, nebo, jak se také říká, o deterministický generátor náhodných binárních sekvencí (Deterministic Random Bit Generator, DRBG ). Implementuje algoritmus zvaný Mersenne Twister, který generuje čísla rozložená takovým způsobem, že výsledek se blíží výsledku skutečného generátoru náhodných čísel. mt_rand() používá pouze jednu náhodnou hodnotu - seed, na jehož základě pevný algoritmus generuje pseudonáhodné hodnoty.


    Podívejte se na tento příklad, můžete si to sami vyzkoušet:


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

    Toto je jednoduchá smyčka spuštěná poté, co byla funkci PHP Mersenne vortex přidělena počáteční, přednastavená hodnota. Byl získán z výstupu funkce uvedené jako příklad v dokumentaci pro mt_srand() a pomocí aktuálních sekund a mikrosekund. Pokud spustíte výše uvedený kód, zobrazí se 25 pseudonáhodných čísel. Vypadají náhodně, žádné náhody, vše je v pořádku. Spusťte kód znovu. Všimli jste si něčeho? Konkrétně: zobrazí se STEJNÁ čísla. Proběhneme to potřetí, počtvrté, popáté. Ve starších verzích PHP může být výsledek jiný, ale to není problém, protože je společný pro všechny moderní verze PHP.


    Pokud útočník získá počáteční hodnotu takového PRNG, bude schopen předpovědět celý výstup mt_rand() . Ochrana počáteční hodnoty je tedy nanejvýš důležitá. Pokud jej ztratíte, již nemáte právo generovat náhodné hodnoty...


    Počáteční hodnotu můžete vygenerovat jedním ze dvou způsobů:

    • ručně pomocí funkce mt_srand()
    • budete mt_srand() ignorovat a necháte PHP, aby jej vygenerovalo automaticky.

    Druhá možnost je vhodnější, ale i dnes starší aplikace často zdědí použití mt_srand(), a to i po přenesení na modernější verze PHP.


    Zvyšuje se tak riziko, že útočník získá zpět počáteční hodnotu (Seed Recovery Attack), což mu poskytne dostatek informací k předpovědi budoucích hodnot. V důsledku toho se jakákoli aplikace po takovém úniku stane zranitelnou vůči útoku na vyzrazení informací. Toto je skutečná zranitelnost, navzdory její zdánlivě pasivní povaze. Únik informací o místním systému může útočníkovi pomoci při následných útocích, které poruší princip obrany do hloubky.

    Náhodné hodnoty v PHP

    PHP používá tři PRNG, a pokud útočník získá přístup k semínkům použitým v jejich algoritmech, bude schopen předvídat výsledky jejich práce:

    1. Lineární kongruenciální generátor (LCG), lcg_value() .
    2. Mersennův vír, mt_rand() .
    3. Lokálně podporovaná funkce C rand() .

    Tyto generátory se také používají interně pro funkce jako array_rand() a uniqid() . To znamená, že útočník může předvídat výstup těchto a dalších funkcí, které používají interní PRNG PHP, pokud získají všechna požadovaná jádra. Znamená to také, že nemůžete zlepšit svou obranu tím, že byste útočníka zmátli několika voláními generátorů. To platí zejména pro aplikace s otevřeným zdrojovým kódem. Útočník je schopen předpovědět VŠECHNY výstupy pro jakoukoli počáteční hodnotu, kterou zná.


    Pro zlepšení kvality generovaných náhodných hodnot pro netriviální úlohy potřebuje PHP externí zdroje entropie poskytované operačním systémem. Na Linuxu se obvykle používá /dev/urandom, můžete jej číst přímo nebo k němu přistupovat nepřímo pomocí funkcí openssl_pseudo_random_bytes() nebo mcrypt_create_iv(). Oba mohou používat kryptograficky bezpečný generátor pseudonáhodných čísel (CSPRNG) na Windows, ale v PHP v uživatelském prostoru zatím neexistuje přímá metoda pro získání dat z tohoto generátoru bez rozšíření poskytovaných těmito funkcemi. Jinými slovy, ujistěte se, že vaše serverová verze PHP má povoleno rozšíření OpenSSL nebo Mcrypt.


    /dev/urandom je PRNG, ale často získává nová semínka z vysoce entropického zdroje /dev/random . To z něj dělá pro útočníka nezajímavý cíl. Snažíme se vyhnout čtení přímo z /dev/random, protože je to blokující zdroj. Pokud mu dojde entropie, všechna čtení budou zablokována, dokud znovu nezíská dostatek entropie ze systémového prostředí. Ačkoli pro nejdůležitější úkoly byste měli použít /dev/random .


    To vše nás vede k pravidlu:


    Všechny procesy, které zahrnují použití netriviálních náhodných čísel, MUSÍ používat openssl_pseudo_random_bytes(). Případně MŮŽETE zkusit číst bajty přímo z /dev/urandom. Pokud nefunguje ani jedna možnost a nemáte na výběr, MUSÍTE hodnotu vygenerovat intenzivním smícháním dat z více dostupných zdrojů náhodných nebo tajných hodnot.

    Základní implementaci tohoto pravidla naleznete v referenční knihovně SecurityMultiTool. Jak už to bývá, interní uživatelé PHP raději ztěžují život programátorům místo přímého začlenění bezpečných řešení do jádra PHP.


    Dost teorie, nyní se podívejme, jak můžete napadnout aplikaci, vyzbrojenou výše uvedeným.

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

    Z mnoha důvodů PHP používá PRNG k řešení netriviálních problémů.


    Funkce openssl_pseudo_random_bytes() byla dostupná pouze v PHP 5.3. Na Windows to způsobovalo problémy s blokováním, dokud nevyšla verze 5.3.4. Také v PHP 5.3 začala funkce mcrypt_create_iv() ve Windows podporovat zdroj MCRYPT_DEV_URANDOM. Dříve Windows podporoval pouze MCRYPT_RAND - v podstatě stejný systém PRNG, který interně používá funkce rand(). Jak můžete vidět, před PHP 5.3 bylo mnoho mezer, takže mnoho starších aplikací napsaných v předchozích verzích možná nepřešlo na silnější PRNG.


    Výběr rozšíření Openssl a Mcrypt je na vašem uvážení. Protože se nelze spolehnout, že budou dostupné i na serverech s PHP 5.3, aplikace často používají PRNG zabudované v PHP jako záložní pro generování netriviálních náhodných hodnot.


    Ale v obou případech máme netriviální problémy, které používají náhodné hodnoty generované PRNG se semeny s nízkou entropií. To nás činí zranitelnými vůči útokům na vyhledávání. Podívejme se na jednoduchý příklad.


    Představme si, že jsme našli online aplikaci, která používá následující kód ke generování tokenů, které se používají v různých úlohách v celé aplikaci:


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

    Existují složitější způsoby generování tokenů, ale toto je dobrá volba. Existuje pouze jedno volání mt_rand(), hašované pomocí SHA512. V praxi, pokud se programátor rozhodne, že funkce náhodné hodnoty PHP jsou „dostatečně náhodné“, pak s největší pravděpodobností zvolí zjednodušující přístup, dokud nepadne slovo „kryptografie“. Například nekryptografické případy zahrnují přístupové tokeny, tokeny CSRF, jednorázové hodnoty API a tokeny pro resetování hesla. Než budu pokračovat, podrobně popíšu celý rozsah zranitelnosti této aplikace, abyste lépe porozuměli tomu, co dělá aplikace zranitelné.

    Charakteristika zranitelné aplikace

    Toto není vyčerpávající seznam. V praxi se výčet vlastností může lišit!

    1. Server používá mod_php, který při použití s ​​KeepAlive umožňuje obsluhovat více požadavků stejným procesem PHP

    To je důležité, protože generátory náhodných čísel v PHP jsou nasazeny pouze jednou za proces. Pokud můžeme odeslat dva nebo více požadavků na proces, pak použije stejnou počáteční hodnotu. Podstatou útoku je využít expanze jednoho tokenu k extrakci seed hodnoty, která je potřebná k predikci dalšího tokenu vygenerovaného na základě STEJNÉ seed hodnoty (tj. ve stejném procesu). Protože mod_php je ideální pro použití více dotazů k načtení souvisejících náhodných hodnot, může být někdy možné načíst více hodnot souvisejících s mt_rand() pouze jedním dotazem. Díky tomu jsou jakékoli požadavky mod_php nadbytečné. Například některá entropie použitá ke generování semene pro mt_rand() může unikat přes ID relace nebo výstupní hodnoty ve stejném požadavku.

    2. Server odhalí tokeny CSRF, tokeny pro resetování hesla nebo tokeny potvrzení účtu vygenerované na základě tokenů mt_rand()

    Abychom extrahovali seed hodnotu, musíme přímo zkontrolovat číslo generované generátory v PHP. A nezáleží ani na tom, jak se používá. Můžeme jej extrahovat z jakékoli dostupné hodnoty, ať už je to výstup mt_rand(), hash CSRF nebo token pro ověření účtu. Vhodné jsou i nepřímé zdroje, ve kterých náhodná hodnota určuje jiné chování na výstupu, které prozrazuje právě tuto hodnotu. Hlavním omezením je, že musí pocházet ze stejného procesu, který generuje druhý token, který se snažíme předpovědět. A to je zranitelnost „zpřístupnění informací“. Jak brzy uvidíme, únik PRNG výstupu může být extrémně nebezpečný. Všimněte si, že zranitelnost není omezena na jednu aplikaci: můžete číst výstup PRNG v jedné aplikaci na serveru a použít jej k určení výstupu v jiné aplikaci na stejném serveru, pokud obě používají stejný proces PHP.

    3. Známý algoritmus generování slabého tokenu

    Můžete si to spočítat:

    • po ponoření se do zdrojů aplikace s otevřeným zdrojovým kódem,
    • podplácení zaměstnance přístupem k osobnímu zdrojovému kódu,
    • nalezení bývalého zaměstnance, který chová zášť vůči svému bývalému zaměstnavateli,
    • nebo jednoduše hádejte, jaký algoritmus by tam mohl být.

    Některé metody generování tokenů jsou zjevnější, některé jsou oblíbenější. Skutečně slabé generování znamená použití jednoho z PHP generátorů náhodných čísel (např. mt_rand()), slabé entropie (žádné jiné zdroje nedefinovaných dat) a/nebo slabého hashování (např. MD5 nebo žádné hashování). Výše diskutovaný příklad kódu má znaky slabé metody generování. Také jsem použil hash SHA512, abych ukázal, že maskování je vždy neuspokojivé řešení. SHA512 je slabé hashování, protože se rychle počítá, což znamená, že útočník může brutálně vynutit vstupní data na jakémkoli CPU nebo GPU neuvěřitelnou rychlostí. A nezapomeňte, že stále platí Moorův zákon, což znamená, že rychlost hrubé síly se bude zvyšovat s každou novou generací CPU/GPU. Proto musí být hesla hašována pomocí nástrojů, jejichž prolomení trvá pevně stanovený čas, bez ohledu na výkon procesoru nebo Moorův zákon.

    Provedení útoku

    Náš útok je docela jednoduchý. V rámci připojení k procesu PHP uspořádáme rychlou relaci a odešleme dva samostatné HTTP požadavky (požadavek A a požadavek B). Relace bude držena serverem, dokud nebude přijat druhý požadavek. Požadavek A je zaměřen na získání nějakého dostupného tokenu, jako je CSRF, token pro resetování hesla (zaslaný útočníkovi poštou) nebo něco podobného. Nezapomeňte na další funkce, jako je inline markup používaný v žádostech o libovolná ID atd. Původní token budeme trápit, dokud nám nedá svou počáteční hodnotu. To vše je součástí útoku obnovy semene: kde semeno má tak malou entropii, že jej lze vynutit hrubou silou nebo vyhledat v předem vypočítané duhové tabulce.


    Dotaz B vyřeší zajímavější problém. Požádejme o obnovení hesla místního správce. To spustí generování tokenu (pomocí náhodného čísla založeného na stejném seedu, který vytáhneme s Požadavkem A, pokud jsou oba požadavky úspěšně odeslány do stejného procesu PHP). Tento token bude uložen v databázi v očekávání okamžiku, kdy správce použije odkaz pro obnovení hesla, který mu byl zaslán e-mailem. Pokud dokážeme extrahovat počáteční hodnotu tokenu z požadavku A, a když víme, jak je generován token z požadavku B, můžeme předpovědět token pro resetování hesla. To znamená, že můžeme následovat odkaz pro obnovení dříve, než administrátor přečte dopis!


    Zde je sled událostí:

    1. Pomocí požadavku A získáme token a provedeme zpětnou analýzu pro výpočet počáteční hodnoty.
    2. Pomocí požadavku B získáme token vygenerovaný na základě stejné počáteční hodnoty. Tento token je uložen v databázi aplikace pro budoucí resetování hesla.
    3. Rozluštíme hash SHA512, abychom získali náhodné číslo vygenerované serverem.
    4. Pomocí výsledné náhodné hodnoty hrubou silou vynutíme počáteční hodnotu, která byla s její pomocí vygenerována.
    5. Seed používáme k výpočtu řady náhodných hodnot, které mohou pravděpodobně tvořit základ tokenu pro resetování hesla.
    6. Tento token(y) používáme k resetování hesla správce.
    7. Získáváme přístup k administrátorskému účtu, bavíme se a těžíme. No, aspoň se bavíme.

    Začněme hackovat...

    Hackování aplikací krok za krokem

    Krok 1. Požádejte A o získání tokenu

    Předpokládáme, že cílový token a token pro resetování hesla závisí na výstupu mt_rand() . Proto je potřeba si ho vybrat. V aplikaci v našem imaginárním scénáři jsou všechny tokeny generovány stejně, takže token CSRF můžeme jednoduše extrahovat a uložit na později.

    Krok 2. Proveďte požadavek B pro obdržení tokenu pro resetování hesla vygenerovaného pro účet správce

    Tato žádost je jednoduchým odesláním formuláře pro obnovení hesla. Token bude uložen do databáze a zaslán uživateli poštou. Tento token musíme správně vypočítat. Pokud jsou specifikace serveru přesné, pak požadavek B používá stejný proces PHP jako požadavek A. Volání mt_rand() tedy použijí v obou případech stejnou počáteční hodnotu. Požadavek A můžete dokonce použít k zachycení tokenu CSRF formuláře pro resetování, abyste umožnili odeslání v zájmu zefektivnění postupu (vyhnutí se zpáteční cestě).

    Krok 3. Hackněte hash SHA512 tokenu přijatého z požadavku A

    SHA512 vzbuzuje mezi programátory úžas: má největší počet z celé rodiny algoritmů SHA-2. Existuje však jeden problém s metodou generování tokenů naší oběti - náhodné hodnoty jsou omezeny pouze na čísla (tj. míra nejistoty nebo entropie je zanedbatelná). Pokud zkontrolujete výstup mt_getrandmax() , zjistíte, že největší náhodné číslo, které může mt_rand() vygenerovat, je 2,147 miliardy a nějaká změna. Díky tomuto omezenému počtu funkcí je SHA512 zranitelný vůči hrubé síle.


    Jen mě neberte za slovo. Pokud máte samostatnou grafickou kartu z jedné z nejnovějších generací, můžete jít následujícím způsobem. Protože hledáme jediný hash, rozhodl jsem se použít báječný nástroj hrubé síly – hashcat-lite. Toto je jedna z nejrychlejších verzí hashcat a je k dispozici pro všechny hlavní operační systémy, včetně Windows.


    Pomocí tohoto kódu vygenerujte token:


    $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žadavku A (obsahuje náhodné číslo, které potřebujeme a je skrytý v hash SHA512) a spustí ho přes 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

    Všechny tyto možnosti znamenají:

    • -m1700: Určuje hashovací algoritmus, kde 1700 znamená SHA512.
    • --pw-min=1: Definuje minimální vstupní délku hašované hodnoty.
    • --pw-max=10: Definuje maximální vstupní délku hašované hodnoty (10 pro mt_rand()).
    • -1?d: určuje, že potřebujeme vlastní slovník pouze čísel (tj. 0-9).
    • -o ./seed.txt: soubor pro zápis výsledků. Na obrazovce se nic nezobrazuje, takže nezapomeňte tuto možnost nastavit!
    • ?d?d?d?d?d?d?d?d?d?d: maska, která určuje formát, který se má použít (všechny číslice do maximálně 10).

    Pokud vše funguje správně a vaše GPU se neroztaví, Hashcat spočítá hashované náhodné číslo během několika minut. Ano, minuty. Již dříve jsem vysvětlil, jak entropie funguje. Podívej se sám. Funkce mt_rand() má tak málo možností, že SHA512 hashe všech hodnot lze ve skutečnosti vypočítat ve velmi krátkém čase. Nemělo tedy smysl hašovat výstup mt_rand() .

    Krok 4. Obnovte počáteční hodnotu pomocí čerstvě hacknutého náhodného čísla

    Jak jsme viděli výše, extrahování jakékoli hodnoty generované mt_rand() z SHA512 trvá jen několik minut. Vyzbrojeni náhodnou hodnotou můžeme spustit další nástroj hrubé síly - php_mt_seed. Tato malá utilita přebírá výstup mt_rand() a po hrubé síle vypočítá počáteční hodnotu, ze které mohla být analýza generována. Stáhněte si aktuální verzi, zkompilujte a spusťte. Pokud máte problémy s kompilací, zkuste starší verzi (u nových jsem měl problémy s virtuálními prostředími).


    ./php_mt_seed

    To může trvat o něco déle než praskání SHA512, protože se to provádí na CPU. Na slušném procesoru utilita najde celý možný rozsah výchozí hodnoty za pár minut. Výsledkem je jedna nebo více možných hodnot (tj. hodnot, které mohly být použity k vytvoření náhodného čísla). Opět vidíme výsledek slabé entropie, pouze tentokrát ve vztahu k PHP generování počátečních hodnot pro funkci Mersenne vortex. Na to, jak byly tyto hodnoty generovány, se podíváme později, takže uvidíte, proč lze hrubou sílu provést tak rychle.


    Předtím jsme tedy používali jednoduché hackerské nástroje dostupné na webu. Jsou zaměřeny na volání mt_rand(), ale ilustrují myšlenku, kterou lze aplikovat na jiné scénáře (například sekvenční volání mt_rand() při generování tokenů). Mějte také na paměti, že rychlost hackování nebrání generování duhových tabulek, které berou v úvahu specifické přístupy ke generování tokenů. Zde je další nástroj, který využívá zranitelnosti mt_rand() a je napsán v Pythonu.

    Krok 5. Vygenerujte možné tokeny pro obnovení hesla účtu správce

    Předpokládejme, že v rámci požadavků A a B byly na mt_rand() vzneseny pouze dva požadavky. Nyní začněme předpovídat tokeny pomocí dříve vypočítaných možných počátečních hodnot:


    funkce predikovat($seed) ( /** * Předat počáteční hodnotu PRNG */ mt_srand($seed); /** * Přeskočit volání funkce z požadavku A */ mt_rand(); /** * Předpovědět a vrátit jeden vygenerovaný v tokenu požadavku B */ $token = hash("sha512", mt_rand()); return $token; )

    Tato funkce předpovídá resetovací token pro každý možný zdroj.

    Kroky 6 a 7: Resetujte heslo k účtu správce a bavte se!

    Nyní musíte shromáždit adresu URL obsahující token, který vám umožní resetovat heslo správce kvůli zranitelnosti aplikace a získat přístup k vašemu účtu. Možná zjistíte, že nefiltrované HTML můžete zveřejňovat na fóru nebo v článku (běžné porušení principu ochrany do hloubky). To vám umožní provést rozsáhlý XSS útok na všechny ostatní uživatele aplikace, infikovat jejich počítače malwarem a monitorováním Man-In-The-Browser. Vážně, proč se zastavit jen u získání přístupu? Smyslem těchto zdánlivě pasivních a nepříliš nebezpečných zranitelností je pomoci útočníkovi pomalu proniknout tam, kde může konečně dosáhnout svého hlavního cíle. Hackování je jako hraní arkádové bojové hry, kde musíte rychle stisknout správnou kombinaci, abyste rozpoutali sérii silných útoků.

    Analýza po útoku

    Výše uvedený scénář a jednoduchost kroků by vám měly jasně ukázat nebezpečí mt_rand() . Rizika jsou tak zřejmá, že nyní můžeme jakékoli slabě skryté výstupní hodnoty mt_rand() přístupné útočníkovi v jakékoli formě považovat za zranitelnost „odhalení informací“.


    Navíc tento příběh má i druhou stránku. Pokud jste například závislí na knihovně, která nevinně používá mt_rand() pro některé důležité úkoly, a to i bez poskytnutí výsledných hodnot, pak použitím „děravého“ tokenu pro vaše potřeby tuto knihovnu kompromitujete. A to je problém, protože knihovna nebo framework nedělá nic pro zmírnění útoku obnovením počáteční hodnoty. Měli bychom vinit uživatele za únik hodnot mt_rand() ​​nebo knihovnu, že nepoužila lepší náhodné hodnoty?


    Ve skutečnosti jsou oba docela vinni. Knihovna by neměla volit mt_rand() (nebo jakýkoli jiný jediný zdroj slabé entropie) pro důležité problémy jako jediný zdroj náhodných hodnot. A uživatel by neměl psát kód, který uniká hodnoty mt_rand(). Takže ano, můžete začít ukazovat obviňujícím prstem na negramotné příklady použití mt_rand() , i když to nepovede k přímým únikům.


    Není to jen zranitelnost při zveřejňování informací, o kterou se musíte starat. Je také důležité si uvědomit nedostatek zranitelností v oblasti entropie, které zanechávají aplikace zranitelné vůči hrubé síle citlivých tokenů, klíčů nebo nonces, které nejsou technicky kryptografické, ale používají se při provozu netriviálních funkcí aplikací.

    A teď je vše při starém

    Nyní víme, že použití PRNG zabudovaných do PHP je považováno za nedostatek zranitelnosti entropie (tj. snížení nejistoty usnadňuje hrubou sílu). Můžeme rozšířit náš útok:


    Kvůli zranitelnosti zpřístupnění informací je tato metoda generování tokenů zcela zbytečná. Abychom pochopili proč, podívejme se blíže na PHP funkci uniqid() . Jeho definice:


    Na základě aktuálního času v mikrosekundách získá jedinečný identifikátor prefixu.


    Jak si pamatujete, entropie je míra nejistoty. Kvůli zranitelnosti při odhalování informací mohou hodnoty generované mt_rand() uniknout, takže použití mt_rand() jako jedinečné předpony identifikátoru přidává nulovou nejistotu. V našem příkladu je jediným dalším druhem vstupu do uniqid() čas. Ale rozhodně to NENÍ vágní. Mění se lineárně a předvídatelně. A předvídatelné hodnoty mají extrémně nízkou entropii.


    Definice se samozřejmě týká „mikrosekund“, tedy miliontin sekundy. To nám dává 1 000 000 možných čísel. Zde ignoruji hodnoty větší než 1 sekunda, protože jejich zlomek a měřitelnost jsou tak velké (například záhlaví HTTP Date v odpovědi), že nedává téměř nic. Než půjdeme do podrobností, rozeberme funkci uniqid() a podívejme se 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 mít maximální hodnotu 0xF423F, takže použijeme * usecs pouze pět hexadecimálních čísel. */ if (more_entropy) ( spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10); ) else ( spprintf(&uniqid, 0, "% s%08x%05x", prefix, sec, usec); ) RETURN_STRING(uniqid, 0);

    Pokud to vypadá příliš složitě, můžete vše replikovat ve starém dobrém 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, $ s, $usec); ))

    Tento kód nám říká, že jednoduché volání uniqid() bez parametrů nám vrátí 13znakový řetězec. Prvních 8 znaků je aktuální časové razítko Unixu (v sekundách), vyjádřené v šestnáctkové soustavě. Posledních 5 znaků jsou další mikrosekundy v šestnáctkové soustavě. Jinými slovy, základní funkce uniqid() poskytuje velmi přesné měření systémového času, které lze získat z jednoduchého volání uniqid() pomocí kódu, jako 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;

    Podívejte se na kód C. Přesný systémový čas není nikdy skrytý ve výstupu, bez ohledu na parametry:


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

    Jedinečné identifikátory hrubé síly

    Po zamyšlení je zřejmé, že odhalení jakékoli hodnoty uniqid() útočníkovi je dalším příkladem potenciální zranitelnosti prozrazení informací. Tím unikne velmi přesný systémový čas, který lze použít k predikci vstupu pro následná volání uniqid(). To pomáhá vyřešit všechna dilemata, která nastanou při pokusu předpovědět mikrosekundy zúžením 1 000 000 možností na užší rozsah. Protože tento únik mohl být zmíněn později, není v našem příkladu technicky potřeba. Podívejme se znovu na původní kód tokenu uniqid():


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

    Z tohoto příkladu můžeme vidět, že provedením resetovacího útoku na mt_rand() v kombinaci se zveřejněním informací z uniqid() vypočítáme relativně malou sadu hashů SHA512, což se může ukázat jako resetování hesla nebo jiné důležité tokeny. Pokud potřebujete úzký rozsah časových razítek, aniž byste zneužili únik systémového času z uniqid() , pak pojďme analyzovat odpovědi serveru, které obvykle obsahují záhlaví HTTP Date. Odtud můžete získat přesná časová razítka serveru. A protože v tomto případě je entropie rovna jednomu milionu možných hodnot mikrosekund, můžete ji brutálně vynutit za pár sekund!


    Zachrání nás rostoucí entropie?

    Samozřejmě je možné přidat entropii do uniqid() nastavením druhého parametru funkce na TRUE:


    Jak ukazuje kód C, nový zdroj entropie používá výstup interní funkce php_combined_lcg(). Tato funkce je vystavena uživatelskému prostoru prostřednictvím funkce lcg_value() , kterou jsem použil při své konverzi PHP funkce uniqid(). V podstatě kombinuje dvě hodnoty generované dvěma lineárními kongruentními generátory s oddělenými počátečními hodnotami. Níže je uveden kód, který dodal generátorům tyto počáteční hodnoty. Stejně jako u mt_rand() jsou generovány jednou za proces PHP a znovu použity ve všech následujících voláních.


    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; }

    Pokud se na to díváte příliš dlouho a chcete něco hodit na monitor, pak je lepší ne. Monitory jsou dnes drahé.


    Obě počáteční hodnoty používají funkci C gettimeofday() k zachycení aktuálního času v sekundách a mikrosekundách od epochy Unixu (s odkazem na hodiny serveru). Nutno podotknout, že obě volání jsou implementována ve zdrojovém kódu, takže hodnota čítače microsecond() mezi nimi bude minimální, což snižuje vnesenou nejistotu. Druhá počáteční hodnota bude také přimíchána do ID aktuálního procesu, které ve většině případů pod Linuxem nepřekročí 32 768. Samozřejmě můžete ručně zvýšit limit na zhruba 4 miliony změnou /proc/sys/kernel/pid_max , ale to je velmi nežádoucí.


    Ukazuje se, že primárním zdrojem entropie používaným těmito LCG jsou mikrosekundy. Pamatujete si například naši počáteční hodnotu mt_rand()? Hádejte, jak se to počítá.


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

    To znamená, že všechny počáteční hodnoty používané v PHP jsou vzájemně závislé. I stejná vstupní data jsou několikrát smíchána. Možná byste mohli omezit rozsah počátečních mikrosekund, jak jsme diskutovali výše: pomocí dvou dotazů, přičemž první bude skákat mezi sekundami (takže mikročas by byl 0 + doba provedení dalšího volání gettimeofday() C). Můžete dokonce vypočítat mikrosekundovou deltu mezi ostatními voláními gettimeofday(), pokud máte přístup ke zdrojovému kódu (pomáhá open source povaha PHP). Nemluvě o tom, že hrubé vynucení semene pomocí mt_rand() vám poskytne finální semeno, které umožňuje offline ověření.


    Hlavní problém však spočívá v php_combined_lcg() . Toto je nízkoúrovňová implementace funkce lcg_value() v uživatelském prostoru, která získá počáteční hodnotu jednou za proces PHP. A pokud tuto hodnotu znáte, můžete předvídat výstup. Pokud tento oříšek rozlousknete, je to, hra je u konce.

    Je na to aplikace...

    Hodně času jsem věnoval praktickým věcem, takže se k nim ještě vraťme. Není tak snadné získat dvě počáteční hodnoty používané php_combined_lcg() - nemusí být možné vytvořit přímý únik. Funkce lcg_value() je poměrně málo známá a programátoři se častěji spoléhají na mt_rand(), když potřebují PRNG zabudované v PHP. Nechci bránit lcg_value() v úniku hodnoty, ale je to neoblíbená funkce. Dvojice kombinovaných LCG také neodráží funkci seedingu (takže nemůžete jen hledat volání mt_srand() k identifikaci děravého seedovacího mechanismu zděděného z něčího staršího kódu). Existuje však spolehlivý zdroj, který přímo poskytuje data pro brute-forceing seed: ID relací v PHP.


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

    Tento kód generuje pre-hash hodnotu pro ID relace pomocí IP, časového razítka, mikrosekund a... php_combined_lcg() výstupu. Vzhledem k výraznému snížení počtu mikročasových možností (tento kód potřebuje 1 pro vygenerování ID a 2 pro php_combined_lcg() , což vede k minimálnímu rozdílu mezi nimi), můžeme to nyní hrubou silou. No, pravděpodobně.


    Jak si možná pamatujete, PHP nyní podporuje nové možnosti relace jako session.entropy_file a session.entropy_length. To se provádí, aby se zabránilo ID relací hrubou silou, během kterých můžete rychle (nebude to trvat hodiny) získat dvě počáteční hodnoty pro generátory LCG kombinované pomocí php_combined_lcg(). Pokud používáte PHP 5.3 nebo nižší, možná jste tyto možnosti nakonfigurovali nesprávně. To znamená, že existuje další užitečná zranitelnost při zveřejňování informací, která vám umožňuje použít ID relací hrubou silou za účelem získání počátečních hodnot pro LCG.


    Pro takové případy existuje aplikace pro Windows, která umožňuje vypočítat hodnoty LCG.


    Mimochodem, znalost stavů LCG vám umožňuje pochopit, jak mt_rand() získává počáteční hodnotu, takže toto je další způsob, jak obejít nedostatek úniků hodnoty mt_rand().


    Co to všechno znamená z hlediska přidání entropie k návratovým hodnotám uniqid() ?


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

    Toto je další příklad potenciální zranitelnosti nedostatku entropie. U úniků se nemůžete spoléhat na entropii (i když za ně nejste odpovědní!). Díky úniku informací o ID relace může útočník také předpovědět hodnotu entropie, která byla k tomuto ID dodatečně přidána.


    Znovu, kdo za to může? Pokud aplikace X spoléhá na uniqid() , ale uživatel nebo jiná aplikace na stejném serveru unikne vnitřnímu stavu LCG, musíte v obou situacích zasáhnout. Uživatelé by si měli být jisti, že ID relací používají dostatečně vysokou entropii, a programátoři třetích stran by měli pochopit, že jejich metody generování náhodných hodnot postrádají entropii, takže je třeba přejít na vhodnější alternativy (i když jsou k dispozici pouze slabé zdroje entropie!) .

    Při hledání entropie

    PHP samo o sobě není schopno generovat silnou entropii. Neexistuje ani základní API pro přenos dat z generátorů PRNG na úrovni operačního systému, které jsou spolehlivými zdroji silné entropie. Proto se musíte spolehnout na volitelná rozšíření openssl a mcrypt. Nabízejí funkce, které jsou mnohem lepší než jejich děravé, předvídatelné, nízkoentropické příbuzné.


    Bohužel, protože obě rozšíření jsou volitelná, v některých případech nemáme jinou možnost, než se jako poslední možnost spolehnout na slabé zdroje entropie. Když k tomu dojde, slabá entropie mt_rand() musí být doplněna o další zdroje nejistoty a smíchat jejich data do jednoho fondu, ze kterého lze čerpat pseudonáhodné bajty. Podobný náhodný generátor využívající směšovač silné entropie již implementoval Anthony Ferrara ve své knihovně RandomLib. To je to, co by programátoři měli dělat, kdykoli je to možné.


    Vyhněte se pokušení skrýt slabost své entropie hašováním složitých matematických transformací. To vše útočník zopakuje, jakmile zjistí primární počáteční hodnotu. Takové triky jen mírně zvýší množství výpočtů při hrubé síle. Nezapomeňte: čím nižší entropie, tím menší nejistota; Čím méně nejistoty je, tím méně příležitostí je třeba brutálně vynucovat. Jediným ospravedlnitelným řešením je zvýšit množství entropie, kterou používáte, jakýmikoli dostupnými prostředky.


    Knihovna RandomLib generuje náhodné bajty smícháním dat z různých zdrojů entropie a lokalizací informací, které může útočník potřebovat k odhadům. Můžete například smíchat výstupy mt_rand(), uniqid() a lcg_value(), přidat PID, spotřebu paměti, nějaké další mikročasové měření, serializaci $_ENV, posix_times() atd. Nebo jít ještě dál, to umožňuje RandomLib rozšiřitelnost. Řekněme, že použijeme nějaké delty v mikrosekundách (tj. změříme, kolik mikrosekund potřebuje funkce k práci s pseudonáhodnými vstupními daty, jako jsou volání hash()).

    Přidat štítky

    Náhodná čísla jsou nedílnou součástí programování, zejména pokud jde o bezpečnostní systémy. Například kryptografie spoléhá na generování náhodných hodnot, aby produkovala čísla, která nelze předvídat. V PHP samozřejmě hrají obrovskou roli náhodná čísla: s jejich pomocí můžeme generovat tokeny, soli a další hodnoty.

    Generování náhodných čísel je založeno na speciálních algoritmech. Vstupním parametrem, od kterého bude algoritmus startovat, může být buď náhodná hodnota, nebo předem určená hodnota.

    V tomto článku budeme hovořit o náhodných číslech: jak se generují a kde je lze použít.

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

    V PHP hrají náhodná čísla obrovskou roli, protože... velmi často používané pro různé účely. V zásadě souvisí s bezpečností. Na jejich základě se generují CSRF tokeny, API klíče, autentizační hodnoty, hodnoty pro resetování hesla a mnoho dalšího. To vše se děje tak, že výslednou hodnotu nelze předvídat.

    Nejdůležitější příklady použití náhodných hodnot jsou:

    • Generování soli pro kryptografii- náhodné číslo soli se zpravidla používá pro jednosměrné šifrování a také pro hashování hesel. Tato náhodná hodnota se používá jako inicializační vektor v kryptografii.
    • Vygenerujte náhodné hodnoty, jako je ID relace- PHP se používá k vytvoření obrovského množství aplikací, kde je bezpečnost na prvním místě. Mnoho funkcí je založeno na práci s relacemi a generovanými ID relace.
    • Generování autentizačních tokenů, které je téměř nemožné předvídat- mnoho aplikací PHP je založeno na spolupráci s jinými systémy prostřednictvím speciálních rozhraní API. Před použitím rozhraní API musíte obvykle projít procesem ověřování. Získání těžko dostupných hodnot pro tokeny je velmi obtížné. Proto se v těchto úlohách používají náhodná čísla.

    Generátory náhodných čísel

    Náhodná čísla použitá ve výše popsaných případech jsou generována pseudogenerátory v PHP. K dispozici je několik algoritmů:

      Lineární kongruentní metoda při použití funkce lcg_value().

      Mersennův vír, používaný funkcí mt_rand().

      Funkce rand() používá podobnou funkci v jazyce C.

    Ve skutečnosti tyto funkce nevrací náhodná čísla, ale čísla distribuovaná takovým způsobem, že se jeví jako náhodná. Posloupnost těchto čísel závisí na základním náhodném čísle v implementovaném algoritmu.

    Základní čísla pro generátory

    Základní čísla nebo vektory základních čísel jsou sady dat, které se používají ke generování náhodných čísel. Generátory pseudonáhodných čísel fungují pouze tak, že od nich začínají. Pokud útočník zná toto základní číslo, bude v budoucnu schopen předpovědět hodnoty vašich náhodných čísel.

    V PHP můžete určit základní čísla dvěma způsoby. První je použití funkce mt_srand(). Tato metoda se používá především v jednotkových testech náhodné řady. Druhý způsob je nechat PHP generovat základní čísla samo. Od verze 4.2 tuto funkci poskytuje PHP. Následně bude Mersenne Vortex použit ke generování náhodných čísel.

    PHP generuje základní číslo v závislosti na operačním systému. Na platformách Linux můžete použít funkce mcrypt_create_iv() nebo openssl_pseudo_random_bytes() v /dev/urandom. Windows poskytuje speciální pseudogenerátor, ke kterému lze přistupovat prostřednictvím funkcí openssl_pseudo_random_bytes() a mcrypt_create_iv().

    Sečteno a podtrženo

    Protože náhodná čísla hrají obrovskou roli při vytváření bezpečných webových aplikací, potřebujeme vědět více o tom, jak fungují. Pokud chcete generovat základní čísla pro pseudogenerátory sami, ujistěte se, že jsou vaše metody spolehlivé.

    • Z:
    • Registrovaný: 2014.07.07
    • Příspěvky: 3,775
    • Mám rád PunBB:
    • 5 roky, 6 měsíce,
    • Líbí se: 463

    Téma: Jak vybrat náhodnou hodnotu z pole v PHP

    Pomocí této funkce můžeme vybrat náhodný prvek (nebo prvky) pole. Ano, přesně ten prvek nebo prvky! Může to být jeden prvek nebo jich může být několik. Vše závisí na úkolu, kterému čelíte.

    Zde je však třeba vzít v úvahu, že funkce nevrátí hodnotu prvku, ale jeho klíč (nebo klíče, pokud je prvků více).

    Funkce bere jako parametry v závorce: název pole, se kterým pracujeme, a počet prvků, které je třeba vybrat.

    Obecně je vše jednoduché! A bude to ještě jednodušší, když se na to vše podíváme na příkladech.

    Nejprve vybereme jeden náhodný prvek z pole.

    2 Odpovědět od PunBB

    • Z: Moskva, Sovchoznay 3, apt. 98
    • Registrovaný: 2014.07.07
    • Příspěvky: 3,775
    • Mám rád PunBB:
    • 5 roky, 6 měsíce,
    • Líbí se: 463

    Představme si, že někde v horní části našeho webu chceme zobrazit nějaké citáty. Uvozovky se samozřejmě musí změnit. Pokaždé, když uživatel navštíví váš web, chcete, aby uživatel viděl novou nabídku.

    Jak jste pravděpodobně uhodli, nejjednodušší způsob, jak to implementovat, je umístit všechny dostupné uvozovky a výroky do pole a poté vybrat náhodný prvek z tohoto pole a zobrazit jej na obrazovce.

    Čím více uvozovek v poli máte, tím menší je pravděpodobnost, že se budou opakovat.

    Ale pro příklad se nebudu příliš obtěžovat a vložím do svého pole 7 výroků.

    Dále budu muset vytvořit proměnnou, do které uložím výsledek funkce array_rand(). V závorkách bude mít tato funkce dva argumenty: název našeho pole a počet náhodných prvků, které potřebujeme.

    Jak jsem již řekl, funkce nevrací hodnotu prvku, ale jeho klíč (nebo číslo v seznamu). Klíč náhodného prvku tedy bude uložen v proměnné.

    Poté už jen potřebuji zobrazit hodnotu požadovaného prvku. K tomu uvádím název pole a v hranatých závorkách název naší proměnné, která obsahuje náhodný klíč.

    To je vše. Podívejte se na níže uvedený kód a myslím, že vše zcela pochopíte:

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

    3 Odpovědět od PunBB

    • Z: Moskva, Sovchoznay 3, apt. 98
    • Registrovaný: 2014.07.07
    • Příspěvky: 3,775
    • Mám rád PunBB:
    • 5 roky, 6 měsíce,
    • Líbí se: 463

    Re: Jak vybrat náhodnou hodnotu z pole v PHP

    Nyní si procvičme tisk několika náhodných prvků pole.

    V případě jednoho prvku je vrácen jeho klíč a v případě několika náhodných prvků pole je vráceno pole klíčů. Z toho budeme vycházet při zobrazování na obrazovce.

    Nejprve si vytvoříme pole, do kterého přidáme 7 různých jmen.

    Dále vytvoříme proměnnou, do které se bude zaznamenávat práce funkce array_rand(). Teprve nyní v závorce pro tuto funkci uvedeme číslo „2“ jako druhý argument. To znamená, že potřebujeme 2 náhodné prvky.

    Výsledkem funkce v této situaci bude pole obsahující dva náhodné klíče prvků z našeho hlavního pole.

    Při zobrazování na obrazovce je tedy potřeba s tím počítat a v hranatých závorkách uvést nejen název proměnné, ale i název proměnné, dále hranaté závorky a index pole. Protože máme 2 prvky, v prvním případě bude index , a ve druhém . (Pamatujete si, že indexování v polích začíná na "0".)

    To je vše. Podívejte se na kód, aby bylo vše jasnější:

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

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

    ";

    V důsledku toho se na obrazovce zobrazí dvě náhodná jména. Při každém obnovení stránky se názvy změní.

    Zdroj