PHP-də massivdən təsadüfi dəyəri necə seçmək olar. Php random: Təsadüfi ədədlərdən istifadə edərək massivdən təsadüfi dəyəri necə əldə etmək olar

element seçin (11)

Məndə $ran = array(1,2,3,4) adlı massiv var;

Mən bu massivdən təsadüfi qiymət almalı və onu dəyişəndə ​​saxlamalıyam, bunu necə edə bilərəm?

Cavablar

Cavabımı @OlafurWaage funksiyasına əsaslanıram. Mən ondan istifadə etməyə çalışdım, lakin geri qaytarılan obyekti dəyişdirməyə çalışarkən kömək problemi ilə üzləşdim. İstinadla keçmək və qayıtmaq üçün onun funksiyasını yeniləmişəm. Yeni xüsusiyyət:

Funksiya &random_value(&$array, $default=null) ( $k = mt_rand(0, count($array) - 1); if (isset($array[$k])) ($array[$k] qaytarın; ) başqa ($default qaytarın; ))

Əlavə məlumat üçün Obyekt İstinadlarının Keçirilməsi/Qayıdılması + Obyektin dəyişdirilməsi İşləmir bölməsində mənim sualıma baxın

$dəyər = $massiv;

Aşağıda göstərildiyi kimi massivinizdən təsadüfi düymə seçmək üçün array_rand funksiyasından istifadə edə bilərsiniz.

$massiv = massiv("bir", "iki", "üç", "dörd", "beş", "altı"); echo $array;

və ya təsadüfi indeksi seçmək üçün rand və count funksiyalarından istifadə edə bilərsiniz.

$massiv = massiv("bir", "iki", "üç", "dörd", "beş", "altı"); echo $array;

$təsadüfi = $random;

Əgər dəyərə ehtiyacınız varsa, bu funksiya kimi də faydalıdır

Funksiya random_value($array, $default=null) ( $k = mt_rand(0, count($array) - 1); qaytarılması isset($massiv[$k])? $massiv[$k]: $default; )

Seçiminizin təhlükəsizlik baxımından hər hansı təsiri varmı? Əgər belədirsə, random_int() və array_keys() istifadə edin. (random_bytes() yalnız PHP 7-dir, lakin PHP 5 üçün çoxlu doldurma var).

Funksiya random_index(massiv $mənbə) ( $max = count($source) - 1; $r = random_int(0, $max); $k = array_keys($mənbə); $k[$r]; qaytarın)

Ərizə:

$array = [ "alma" => 1234, "oğlan" => 2345, "pişik" => 3456, "it" => 4567, "echo" => 5678, "bəxt" => 6789 ]; $i = təsadüfi_index($massiv); var_dump([$i, $massiv[$i]]);

Funksiya array_random($massiv, $miqdar = 1) ( $açarlar = massiv_rand($massiv, $miqdar); əgər ($miqdar == 1) ( $massiv[$düymələr]; ) $nəticələr = ; foreach ($düymələr) $açar kimi) ( $nəticələr = $massiv[$key]; ) $nəticələri qaytarın)

Ərizə:

$items = ["foo", "bar", "baz", "lorem"=>"ipsum"]; massiv_təsadüfi($maddələr); // "bar" array_random($items, 2); // ["foo", "ipsum"]

Bir neçə qeyd:

  • $miqdar count($array) dəyərindən az və ya ona bərabər olmalıdır.
  • array_rand() düymələri qarışdırmır (PHP 5.2.10-dan bəri, bax), ona görə də seçdiyiniz elementlər həmişə orijinal qaydada olacaq. Lazım gələrsə, shuffle() funksiyasından istifadə edin.

redaktə: Laravel funksiyası o vaxtdan bəri xeyli böyüdü, bax Laravel 5.4"s Arr::random() . Burada yetkin Laravel funksiyasından irəli gələn daha mürəkkəb bir şey var:

array_random funksiyası($array, $number = null) ( $requested = ($number === null) ? 1: $number; $count = count($array); if ($requested > $count) ( at new \ RangeException("Siz ($requested) maddələr tələb etdiniz, lakin yalnız ($count) elementlər mövcuddur." ) if ($number === null) ( return $array; ) if ((int) $number == = 0 ) ( return ; ) $keys = (massiv) array_rand($massiv, $nəticə = foreach ($açar olaraq) ( $nəticələr = $massiv[$key]; ) qaytarmaq $nəticələr;

Bir neçə məqam:

  • Kifayət qədər mövcud element yoxdursa, istisna atın
  • array_random($array, 1) bir elementdən ibarət massivi qaytarır (#19826)
  • Elementlərin sayı üçün dəstək dəyəri "0" (Bu, sadəcə təmiz və sadədir.

Artıq bir neçə dəfə məndən necə olduğumu soruşublar sitatların təsadüfi çıxışı blokdakı veb saytımda " Ağıllı Sitatlar". Sonra, burada problemin insanların anlaşılmazlığı ilə bağlı olduğunu öyrənə bildim. PHP-də massivdən təsadüfi elementi necə əldə etmək olar. Tapşırıq sadədir, lakin buna baxmayaraq, suallar yarandığı üçün onlara cavab verilməlidir.

Mən sizə kodu dərhal verəcəyəm. Deyək ki, sitatlar dəsti olan massiv var. Və onlardan birini təsadüfi seçib çıxartmalısınız:

$quotes = massiv(); // Boş massivi işə salın
$quotes = "Fikirlərinizə diqqətli olun, onlar hərəkətlərin başlanğıcıdır."; // İlk sitat
$quotes = "Ən ağıllı və ya ən güclü deyil, dəyişməyə ən həssas olanlar sağ qalır."; // İkinci sitat
$quotes = "Həyat bir dağdır: yavaş-yavaş qalxırsan, tez enirsən."; // Üçüncü sitat
$quotes = "İnsanlar varlı olmaq istəmir, insanlar başqalarından zəngin olmaq istəyirlər."; // Dördüncü sitat
$nömrə = mt_rand(0, say($quotes) - 1); // 0-dan (massiv uzunluğu mənfi 1) daxil olmaqla təsadüfi bir ədəd götürün
echo $quotes[$number]; // Sitat çıxarın
?>

Əsas məqam budur təsadüfi nömrə əldə etmək. Sizə lazım olan tək şey düzgün sərhədləri təyin etməkdir. Massivin bütün uzunluğu boyunca təsadüfi bir element seçmək lazımdırsa, bu, haradandır 0 əvvəl ( massiv uzunluğu mənfi 1). Və sonra sadəcə nəticədə təsadüfi indeksi olan massivdən element çəkin.

Sitatlarla tapşırığa gəldikdə, onları verilənlər bazasında saxlamaq daha yaxşıdır. Prinsipcə, sayt çox sadədirsə, o zaman mətn faylında edilə bilər. Ancaq verilənlər bazasındadırsa, istifadə etmək daha yaxşıdır RAND()LIMIT V SQL sorğusu, belə ki, siz dərhal verilənlər bazasından tək və təsadüfi sitat alırsınız.


Təsadüfi dəyərlər PHP-də hər yerdə var. Bütün çərçivələrdə, bir çox kitabxanada. Çox güman ki, siz özünüz bir ton kod yazmısınız ki, o, tokenlər və duzlar yaratmaq və funksiyalara giriş kimi təsadüfi dəyərlərdən istifadə edir. Həmçinin, təsadüfi dəyərlər müxtəlif problemlərin həllində mühüm rol oynayır:

  1. Hovuzdan və ya məlum variantlardan təsadüfi seçim etmək.
  2. Şifrələmə üçün başlatma vektorlarını yaratmaq.
  3. Avtorizasiya zamanı gözlənilməz ayələr və ya birdəfəlik dəyərlər yaratmaq.
  4. Sessiya identifikatorları kimi unikal identifikatorlar yaratmaq üçün.

Bütün bu hallarda xarakterik bir zəiflik var. Təcavüzkar Təsadüfi Nömrələr Generatorunun (RNG) və ya Yalançı Təsadüfi Nömrələrin Generatorunun (PRNG) çıxışını təxmin edərsə və ya təxmin edərsə, o, bu generator tərəfindən yaradılan tokenləri, duzları, qeyri-nüansları və kriptoqrafik başlatma vektorlarını hesablaya biləcək. Buna görə də yüksək keyfiyyətli təsadüfi dəyərləri, yəni proqnozlaşdırmaq olduqca çətin olanları yaratmaq çox vacibdir. Heç vaxt parol sıfırlama tokenlərini, CSRF tokenlərini, API açarlarını, nonces və ya avtorizasiya nişanlarını proqnozlaşdırıla bilən etməyin!


PHP-də təsadüfi dəyərlərlə əlaqəli daha iki potensial zəiflik var:

  1. Məlumatın Açıqlanması.
  2. Qeyri-kafi entropiya.

Bu kontekstdə "məlumatların açıqlanması" psevdor-təsadüfi nömrə generatorunun daxili vəziyyətinin - onun toxum dəyərinin sızmasına aiddir. Bu kimi sızmalar gələcək PRNG çıxışlarının proqnozlaşdırılmasını çox asanlaşdıra bilər.


"Entropiyanın olmaması" bir PRNG-nin ilkin daxili vəziyyətinin (toxumunun) dəyişkənliyinin və ya onun çıxışının o qədər kiçik olduğu bir vəziyyəti təsvir edir ki, mümkün dəyərlərin bütün diapazonu kobud gücə nisbətən asandır. PHP proqramçıları üçün o qədər də yaxşı xəbər deyil.


Hər iki zəifliyi nümunə hücum ssenariləri ilə ətraflı nəzərdən keçirəcəyik. Ancaq əvvəlcə PHP proqramlaşdırmasına gəldikdə təsadüfi dəyərin əslində nə olduğunu anlayaq.

Təsadüfi dəyərlər nə edir?

Təsadüfi dəyişənlərin məqsədi ilə bağlı çaşqınlıq ümumi anlaşılmazlıqla müşayiət olunur. Şübhəsiz ki, kriptoqrafik cəhətdən güclü təsadüfi dəyərlərlə "digər istifadələr üçün" qeyri-müəyyən "unikal" dəyərlər arasındakı fərq haqqında eşitmisiniz. Əsas təəssürat ondan ibarətdir ki, kriptoqrafiyada istifadə olunan təsadüfi dəyərlər yüksək keyfiyyətli təsadüfilik (daha doğrusu, yüksək entropiya) tələb edir, digər tətbiqlər üçün isə dəyərlər daha az entropiya ilə əldə edilə bilər. Mən bu təəssüratı yanlış və əks məhsuldar hesab edirəm. Gözlənilməz təsadüfi dəyərlərlə əhəmiyyətsiz tapşırıqlar üçün lazım olanlar arasındakı əsl fərq, sonuncunun proqnozlaşdırılmasının zərərli nəticələrə səbəb olmamasıdır. Bu, kriptoqrafiyanı tamamilə istisna edir. Başqa sözlə, qeyri-trivial problemdə təsadüfi bir dəyər istifadə etsəniz, avtomatik olaraq daha güclü RNG-ləri seçməlisiniz.


Təsadüfi dəyərlərin gücü onları yaratmaq üçün sərf olunan entropiya ilə müəyyən edilir. Entropiya "bit" ilə ifadə edilən qeyri-müəyyənlik ölçüsüdür. Məsələn, mən binar bit götürsəm, onun dəyəri 0 və ya 1 ola bilər. Təcavüzkar dəqiq dəyəri bilmirsə, onda bizim 2 bitlik entropiyamız var (yəni, sikkə atma). Təcavüzkar dəyərin həmişə 1 olduğunu bilirsə, onda bizim 0 bit entropiyamız var, çünki proqnozlaşdırıla bilənlik qeyri-müəyyənliyin antonimidir. Həmçinin, bitlərin sayı 0-dan 2-ə qədər dəyişə bilər. Məsələn, əgər ikili bitin 99%-i 1-dirsə, entropiya 0-dan bir qədər böyük ola bilər. Beləliklə, biz nə qədər qeyri-müəyyən ikili bit seçsək, bir o qədər yaxşıdır.


PHP-də bunu daha aydın görmək olar. mt_rand() funksiyası təsadüfi qiymətlər yaradır, bunlar həmişə ədədlərdir. Hərfləri, xüsusi simvolları və ya digər mənaları göstərmir. Bu o deməkdir ki, hər bayt üçün təcavüzkar daha az təxminlərə, yəni aşağı entropiyaya malikdir. Linux mənbəyindən /dev/random baytlarını oxumaqla mt_rand() funksiyasını əvəz etsək, biz həqiqətən təsadüfi bayt alırıq: onlar sistem qurğularının sürücüləri və digər mənbələr tərəfindən yaradılan səs-küyə əsasən yaradılır. Aydındır ki, bu seçim daha yaxşıdır, çünki daha çox entropiyanı təmin edir.


mt_rand() funksiyasının arzuolunmazlığı həm də onunla göstərilir ki, o, həqiqətən təsadüfi deyil, psevdo-təsadüfi ədədlərin generatoru və ya belə deyildiyi kimi, təsadüfi ikili ardıcıllığın deterministik generatorudur (Deterministic Random Bit Generator, DRBG). ). O, Mersenne Twister adlı alqoritmi həyata keçirir və nəticə həqiqi təsadüfi ədədlər generatorunun nəticəsinə yaxın olacaq şəkildə paylanmış ədədlər yaradır. mt_rand() yalnız bir təsadüfi dəyərdən istifadə edir - ona əsaslanan toxum, sabit alqoritm psevdo-təsadüfi dəyərlər yaradır;


Bu nümunəyə nəzər salın, özünüz sınaqdan keçirə bilərsiniz:


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

Bu, PHP Mersenne burulğan funksiyasına ilkin, əvvəlcədən təyin edilmiş dəyər verildikdən sonra icra edilən sadə dövrədir. O, mt_srand() üçün sənədlərdə nümunə kimi verilmiş funksiyanın çıxışından və cari saniyə və mikrosaniyələrdən istifadə etməklə əldə edilmişdir. Yuxarıdakı kodu işlətsəniz, 25 psevdo-təsadüfi rəqəm göstərəcək. Təsadüfi görünürlər, heç bir təsadüf yoxdur, hər şey qaydasındadır. Kodu yenidən işə salaq. Bir şey hiss etdinizmi? Məhz: EYNİ nömrələr göstərilir. Gəlin üçüncü, dördüncü, beşinci dəfə işləyək. PHP-nin köhnə versiyalarında nəticə fərqli ola bilər, lakin bu, PHP-nin bütün müasir versiyaları üçün ümumi olduğu üçün problem deyil.


Təcavüzkar belə bir PRNG-nin toxum dəyərini əldə edərsə, o zaman mt_rand() məhsulunun bütün çıxışını proqnozlaşdıra biləcək. Beləliklə, ilkin dəyəri qorumaq çox vacibdir. Əgər onu itirsəniz, artıq təsadüfi dəyərlər yaratmaq hüququnuz yoxdur...


İlkin dəyəri iki yoldan biri ilə yarada bilərsiniz:

  • mt_srand() funksiyasından istifadə edərək əl ilə,
  • mt_srand() funksiyasına məhəl qoymayacaqsınız və PHP-yə onu avtomatik olaraq yaratmağa icazə verəcəksiniz.

İkinci seçimə üstünlük verilir, lakin bu gün də köhnə proqramlar çox vaxt PHP-nin daha müasir versiyalarına köçürdükdən sonra da mt_srand() istifadəsini miras alır.


Bu, təcavüzkarın gələcək dəyərləri proqnozlaşdırmaq üçün ona kifayət qədər məlumat verəcək ilkin dəyəri (Seed Recovery Attack) bərpa etməsi riskini artırır. Nəticədə, belə bir sızmadan sonra hər hansı proqram məlumatın açıqlanması hücumuna məruz qalır. Görünən passiv təbiətinə baxmayaraq, bu, real zəiflikdir. Yerli sistem haqqında məlumatların sızması hücumçuya sonrakı hücumlarda kömək edə bilər ki, bu da müdafiə prinsipini dərindən pozacaq.

PHP-də təsadüfi dəyərlər

PHP üç PRNG-dən istifadə edir və əgər təcavüzkar onların alqoritmlərində istifadə olunan toxumlara giriş əldə edərsə, o, onların işinin nəticələrini proqnozlaşdıra biləcək:

  1. Xətti Konqruensial Generator (LCG), lcg_value() .
  2. Mersenne burulğanı, mt_rand() .
  3. Yerli olaraq dəstəklənən C funksiyası rand() .

Bu generatorlar həmçinin array_rand() və uniqid() kimi funksiyalar üçün daxili istifadə olunur. Bu o deməkdir ki, təcavüzkar bütün lazımi toxumları əldə edərsə, PHP-nin daxili PRNG-lərindən istifadə edən bu və digər funksiyaların çıxışını proqnozlaşdıra bilər. Bu həm də o deməkdir ki, siz təcavüzkarı generatorlara çoxsaylı zənglərlə qarışdırmaqla müdafiənizi təkmilləşdirə bilməyəcəksiniz. Bu xüsusilə açıq mənbə proqramları üçün doğrudur. Təcavüzkar ona məlum olan hər hansı ilkin dəyər üçün BÜTÜN çıxışları proqnozlaşdıra bilir.


Qeyri-trivial tapşırıqlar üçün yaradılan təsadüfi dəyərlərin keyfiyyətini yaxşılaşdırmaq üçün PHP əməliyyat sistemi tərəfindən təmin edilən xarici entropiya mənbələrinə ehtiyac duyur. Linux-da adətən /dev/urandom istifadə olunur, siz onu birbaşa oxuya və ya openssl_pseudo_random_bytes() və ya mcrypt_create_iv() funksiyalarından istifadə edərək dolayı yolla daxil ola bilərsiniz. Onların hər ikisi Windows-da kriptoqrafik cəhətdən təhlükəsiz psevdo-təsadüfi nömrə generatorundan (CSPRNG) istifadə edə bilər, lakin istifadəçi məkanında PHP-də bu funksiyalar tərəfindən təmin edilən genişləndirmələr olmadan bu generatordan məlumat almaq üçün birbaşa üsul hələ mövcud deyil. Başqa sözlə, PHP-nin server versiyanızda OpenSSL və ya Mcrypt genişlənməsinin aktiv olduğundan əmin olun.


/dev/urandom PRNG-dir, lakin tez-tez yüksək entropiya mənbəyindən yeni toxumlar alır /dev/random . Bu, onu təcavüzkar üçün maraqsız hədəfə çevirir. Biz birbaşa /dev/random-dan oxumaqdan çəkinməyə çalışırıq, çünki bu, bloklayıcı resursdur. Əgər onun entropiyası bitərsə, sistem mühitindən yenidən kifayət qədər entropiya qazanana qədər bütün oxumalar bloklanacaq. Baxmayaraq ki, ən vacib tapşırıqlar üçün /dev/random istifadə etməlisiniz.


Bütün bunlar bizi qaydaya aparır:


Qeyri-trivial təsadüfi ədədlərin istifadəsini əhatə edən bütün proseslər openssl_pseudo_random_bytes() istifadə etməlidir. Alternativ olaraq, siz /dev/urandom-dan baytları birbaşa oxumağa cəhd edə bilərsiniz. Əgər seçimlərdən heç biri işləmirsə və seçiminiz yoxdursa, onda siz təsadüfi və ya gizli dəyərlərin çoxsaylı mövcud mənbələrindən məlumatları ciddi şəkildə qarışdırmaqla dəyəri yaratmalısınız.

Bu qaydanın əsas tətbiqini SecurityMultiTool istinad kitabxanasında tapa bilərsiniz. Həmişə olduğu kimi, PHP daxili istifadəçiləri PHP nüvəsinə təhlükəsiz həlləri birbaşa daxil etmək əvəzinə proqramçıların həyatını çətinləşdirməyə üstünlük verirlər.


Kifayət qədər nəzəriyyə, indi yuxarıdakılarla silahlanmış bir tətbiqə necə hücum edə biləcəyinizi görək.

PHP-də təsadüfi ədəd generatorlarına hücum

Bir sıra səbəblərə görə PHP qeyri-trivial problemləri həll etmək üçün PRNG-lərdən istifadə edir.


openssl_pseudo_random_bytes() funksiyası yalnız PHP 5.3-də mövcud idi. Windows-da 5.3.4 versiyası çıxana qədər bloklama problemlərinə səbəb oldu. Həmçinin PHP 5.3-də Windows-da mcrypt_create_iv() funksiyası MCRYPT_DEV_URANDOM mənbəyini dəstəkləməyə başladı. Əvvəllər Windows yalnız MCRYPT_RAND-ı dəstəkləyirdi - mahiyyətcə rand() funksiyası tərəfindən daxili istifadə edilən eyni sistem PRNG. Gördüyünüz kimi, PHP 5.3-dən əvvəl çoxlu boşluqlar var idi, ona görə də əvvəlki versiyalarda yazılmış bir çox köhnə proqramlar daha güclü PRNG-lərə keçməmiş ola bilər.


Openssl və Mcrypt uzantılarının seçimi sizin ixtiyarınızdadır. Onların hətta PHP 5.3 ilə işləyən serverlərdə də mövcud olduğuna inanmaq mümkün olmadığından, proqramlar tez-tez qeyri-trivial təsadüfi dəyərlər yaratmaq üçün ehtiyat kimi PHP-də quraşdırılmış PRNG-lərdən istifadə edirlər.


Ancaq hər iki halda, aşağı entropiya toxumları olan PRNG-lər tərəfindən yaradılan təsadüfi dəyərlərdən istifadə edən qeyri-trivial problemlərimiz var. Bu, bizi axtarış hücumlarına qarşı həssas edir. Sadə bir misala baxaq.


Təsəvvür edək ki, biz proqram boyu müxtəlif tapşırıqlarda istifadə olunan tokenlər yaratmaq üçün aşağıdakı kodu istifadə edən onlayn proqram tapdıq:


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

Ayələr yaratmaq üçün daha mürəkkəb vasitələr var, lakin bu yaxşı seçimdir. SHA512 ilə hashed edilmiş mt_rand() üçün yalnız bir zəng var. Praktikada proqramçı PHP-nin təsadüfi dəyər funksiyalarının “kifayət qədər təsadüfi” olduğuna qərar verərsə, o zaman “kriptoqrafiya” sözü qeyd olunana qədər çox güman ki, sadə yanaşmanı seçəcəklər. Məsələn, kriptoqrafik olmayan hallara giriş tokenləri, CSRF tokenləri, API birdəfəlik dəyərləri və parol sıfırlama nişanları daxildir. Davam etməzdən əvvəl mən bu tətbiqin zəifliyinin tam ölçüsünü təfərrüatlı şəkildə izah edəcəyəm ki, siz ilk növbədə tətbiqləri nəyin həssas etdiyini daha yaxşı başa düşəsiniz.

Zəif tətbiqin xüsusiyyətləri

Bu tam siyahı deyil. Praktikada xüsusiyyətlərin siyahısı fərqli ola bilər!

1. Server mod_php-dən istifadə edir ki, bu da KeepAlive ilə istifadə edildikdə eyni PHP prosesi ilə birdən çox sorğuya xidmət göstərməyə imkan verir.

Bu vacibdir, çünki PHP-də təsadüfi ədəd generatorları hər prosesdə yalnız bir dəfə səpilir. Prosesə iki və ya daha çox sorğu verə bilsək, o, eyni ilkin dəyərdən istifadə edəcək. Hücumun mahiyyəti toxum dəyərini çıxarmaq üçün bir tokenin genişləndirilməsindən istifadə etməkdir ki, bu da EYNİ toxum dəyərinə (yəni eyni prosesdə) əsaslanan başqa bir işarəni proqnozlaşdırmaq üçün lazımdır. Mod_php əlaqəli təsadüfi dəyərləri əldə etmək üçün çoxsaylı sorğulardan istifadə etmək üçün ideal olduğundan, bəzən yalnız bir sorğu ilə mt_rand() ilə əlaqəli birdən çox dəyəri əldə etmək mümkün ola bilər. Bu, istənilən mod_php tələblərini lazımsız edir. Məsələn, mt_rand() üçün toxum yaratmaq üçün istifadə olunan entropiyanın bəziləri eyni sorğuda sessiya identifikatorları və ya çıxış dəyərlərindən sıza bilər.

2. Server mt_rand() tokenləri əsasında yaradılan CSRF tokenlərini, parol sıfırlama nişanlarını və ya hesab təsdiqləmə nişanlarını aşkar edir

Toxum dəyərini çıxarmaq üçün PHP-də generatorlar tərəfindən yaradılan nömrəni birbaşa yoxlamaq lazımdır. Və onun necə istifadə olunduğunun heç bir əhəmiyyəti yoxdur. Biz onu istənilən mövcud dəyərdən çıxara bilərik, istər mt_rand() çıxışı, istərsə də hashed CSRF və ya hesab doğrulama nişanı. Hətta dolayı mənbələr də uyğundur, burada təsadüfi bir dəyər çıxışda fərqli bir davranışı təyin edir və bu, məhz bu dəyəri ortaya qoyur. Əsas məhdudiyyət ondan ibarətdir ki, bu, proqnozlaşdırmağa çalışdığımız ikinci işarəni yaradan eyni prosesdən olmalıdır. Və bu, “məlumatların açıqlanması” zəifliyidir. Tezliklə görəcəyimiz kimi, sızan PRNG çıxışı son dərəcə təhlükəli ola bilər. Nəzərə alın ki, boşluq tək bir proqramla məhdudlaşmır: siz serverdəki bir proqramda PRNG çıxışını oxuya və hər ikisi eyni PHP prosesindən istifadə etdiyi halda ondan eyni serverdəki başqa proqramda çıxışı müəyyən etmək üçün istifadə edə bilərsiniz.

3. Məlum zəif token generasiya alqoritmi

Siz hesablaya bilərsiniz:

  • açıq mənbə tətbiqinin mənbələrini araşdıraraq,
  • işçinin şəxsi mənbə koduna çıxışı üçün rüşvət vermək,
  • keçmiş işəgötürənə qarşı kin bəsləyən keçmiş işçini tapmaq,
  • və ya sadəcə olaraq hansı alqoritmin ola biləcəyini təxmin etmək.

Bəzi token yaratma üsulları daha açıqdır, bəziləri daha populyardır. Həqiqətən zəif nəsil PHP-nin təsadüfi ədəd generatorlarından (məsələn, mt_rand()), zəif entropiyadan (müəyyən edilməmiş məlumatların başqa mənbələri yoxdur) və/yaxud zəif heshingdən (məsələn, MD5 və ya ümumiyyətlə heşinq etməməkdən) birini istifadə etmək deməkdir. Yuxarıda müzakirə edilən kod nümunəsi zəif nəsil metodunun əlamətlərinə malikdir. Maskanın həmişə qeyri-qənaətbəxş bir həll olduğunu nümayiş etdirmək üçün SHA512 heşinqindən də istifadə etdim. SHA512 zəif heşinqdir, çünki hesablamaq tezdir, yəni təcavüzkar istənilən CPU və ya GPU-da daxil olan məlumatı inanılmaz sürətlə zorla zorlaya bilər. Unutmayın ki, Mur qanunu hələ də qüvvədədir, yəni hər yeni nəsil CPU/GPU ilə kobud güc sürəti artacaq. Buna görə də, parollar prosessorun performansından və ya Mur qanunundan asılı olmayaraq, sındırılması müəyyən vaxt tələb edən alətlərdən istifadə edilməklə heşləşdirilməlidir.

Hücumun həyata keçirilməsi

Bizim hücumumuz olduqca sadədir. PHP prosesinə qoşulmanın bir hissəsi olaraq, biz sürətli sessiya keçirəcəyik və iki ayrı HTTP sorğusu göndərəcəyik (sorğu A və sorğu B). Seans ikinci sorğu alınana qədər server tərəfindən keçiriləcək. Sorğu A, CSRF, parol sıfırlama nişanı (poçtla təcavüzkara göndərilir) və ya buna bənzər bir şey kimi bəzi mövcud nişanları əldə etməyə yönəlmişdir. İxtiyari identifikatorlar üçün sorğularda istifadə olunan daxili işarələmə və s. Bütün bunlar toxumun bərpası hücumunun bir hissəsidir: toxum o qədər az entropiyaya malikdir ki, onu kobud şəkildə məcbur etmək və ya əvvəlcədən hesablanmış göy qurşağı cədvəlində axtarmaq olar.


B sorğusu daha maraqlı problemi həll edəcək. Yerli administrator parolunu sıfırlamaq üçün sorğu verək. Bu, işarənin yaradılmasını işə salacaq (hər iki sorğu uğurla eyni PHP prosesinə göndərilərsə, A Sorğunu ilə çəkdiyimiz eyni toxum əsasında təsadüfi nömrədən istifadə etməklə). Bu nişan inzibatçının ona e-poçt vasitəsilə göndərilən parol sıfırlama linkindən istifadə edəcəyi anı gözləməklə verilənlər bazasında saxlanılacaq. Əgər biz A sorğusundan token üçün toxum dəyərini çıxara bilsək, B sorğusundan tokenin necə yaradıldığını bilməklə parol sıfırlama işarəsini təxmin edə bilərik. Bu o deməkdir ki, administrator məktubu oxumazdan əvvəl sıfırlama linkini izləyə bilərik!


Hadisələrin ardıcıllığını təqdim edirik:

  1. A sorğusundan istifadə edərək, biz işarəni əldə edirik və ilkin dəyəri hesablamaq üçün onu tərs mühəndisləşdiririk.
  2. B sorğusundan istifadə edərək, eyni ilkin dəyər əsasında yaradılan bir işarə alırıq. Bu nişan gələcək parol sıfırlaması üçün proqram verilənlər bazasında saxlanılır.
  3. Server tərəfindən yaradılan təsadüfi nömrəni əldə etmək üçün SHA512 hash-i sındırırıq.
  4. Yaranan təsadüfi dəyərdən istifadə edərək, onun köməyi ilə yaradılan ilkin dəyəri kobud şəkildə tətbiq edirik.
  5. Parol sıfırlama işarəsinin əsasını təşkil edə biləcək bir sıra təsadüfi dəyərləri hesablamaq üçün toxumdan istifadə edirik.
  6. Biz administrator parolunu sıfırlamaq üçün bu nişan(lar)dan istifadə edirik.
  7. Biz administrator hesabına giriş əldə edirik, əylənirik və faydalanırıq. Yaxşı, heç olmasa əylənirik.

Hack etməyə başlayaq...

Addım-addım proqram hacking

Addım 1. Tokeni əldə etmək üçün A sorğusunu edin

Güman edirik ki, hədəf nişanı və parol sıfırlama nişanı mt_rand() çıxışından asılıdır. Buna görə də onu seçmək lazımdır. Xəyali ssenarimizdəki tətbiqdə bütün tokenlər eyni şəkildə yaradılır, ona görə də biz sadəcə CSRF işarəsini çıxarıb sonraya saxlaya bilərik.

Addım 2. Administrator hesabı üçün yaradılan parol sıfırlama nişanını almaq üçün B sorğusunu yerinə yetirin

Bu sorğu parol sıfırlama formasının sadə təqdimatıdır. Token verilənlər bazasında saxlanılacaq və istifadəçiyə poçtla göndəriləcək. Bu işarəni düzgün hesablamalıyıq. Əgər server spesifikasiyaları dəqiqdirsə, onda B sorğusu A sorğusu ilə eyni PHP prosesindən istifadə edir. Buna görə də, mt_rand() funksiyasına edilən zənglər hər iki halda eyni ilkin dəyərdən istifadə edəcək. Siz hətta proseduru sadələşdirmək (aralıq gediş-gəlişdən qaçmaq) məqsədilə təqdimetməni aktivləşdirmək üçün sıfırlama formasının CSRF işarəsini tutmaq üçün A Sorğunu istifadə edə bilərsiniz.

Addım 3. A sorğusundan alınan tokenin SHA512 heşini sındırın

SHA512 proqramçılar arasında heyranlıq yaradır: o, bütün SHA-2 alqoritmlər ailəsində ən çox sayına malikdir. Bununla belə, qurbanımızın token yaratma metodunda bir problem var - təsadüfi dəyərlər yalnız rəqəmlərlə məhdudlaşır (yəni qeyri-müəyyənlik dərəcəsi və ya entropiya əhəmiyyətsizdir). Əgər mt_getrandmax() çıxışını yoxlasanız, mt_rand() funksiyasının yarada biləcəyi ən böyük təsadüfi ədədin 2,147 milyard olduğunu və bir qədər dəyişiklik olduğunu görərsiniz. Bu məhdud sayda funksiya SHA512-ni kobud gücə qarşı həssas edir.


Sadəcə mənim sözümü qəbul etmə. Ən son nəsillərdən birindən diskret video kartınız varsa, aşağıdakı yolla gedə bilərsiniz. Biz tək bir hash axtardığımız üçün mən gözəl brute force alətindən - hashcat-lite-dən istifadə etmək qərarına gəldim. Bu, hashcat-ın ən sürətli versiyalarından biridir və Windows daxil olmaqla bütün əsas əməliyyat sistemləri üçün əlçatandır.


Token yaratmaq üçün bu kodu istifadə edin:


$rand = mt_rand(); echo "Təsadüfi Nömrə: ", $rand, PHP_EOL; $token = hash("sha512", $rand); echo "Token: ", $token, PHP_EOL;

Bu kod A sorğusundan işarəni təkrarlayır (o, bizə lazım olan təsadüfi nömrəni ehtiva edir və SHA512 hash-də gizlənir) və onu hashcat vasitəsilə idarə edir:


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

Bütün bu variantlar nə deməkdir:

  • -m1700: Hashing alqoritmini təyin edir, burada 1700 SHA512 deməkdir.
  • --pw-min=1: Hashed dəyərinin minimum giriş uzunluğunu müəyyən edir.
  • --pw-max=10: Hashed dəyərinin maksimum giriş uzunluğunu müəyyən edir (mt_rand() üçün 10).
  • -1?d: bizə sadəcə rəqəmlərdən (yəni 0-9) ibarət fərdi lüğətə ehtiyacımız olduğunu bildirir.
  • -o ./seed.txt: nəticələri yazmaq üçün fayl. Ekranda heç nə göstərilmir, ona görə də bu seçimi qurmağı unutmayın!
  • ?d?d?d?d?d?d?d?d?d: istifadə ediləcək formatı təyin edən maska ​​(maksimum 10-a qədər bütün rəqəmlər).

Hər şey düzgün işləyirsə və GPU-nuz əriməzsə, Hashcat bir neçə dəqiqə ərzində hash edilmiş təsadüfi nömrəni hesablayacaq. Bəli, dəqiqələr. Daha əvvəl entropiyanın necə işlədiyini izah etdim. Özünüz baxın. mt_rand() funksiyasının o qədər az imkanları var ki, bütün dəyərlərin SHA512 hashlərini çox qısa müddətdə hesablamaq olar. Beləliklə, mt_rand() çıxışını heşləşdirməyin mənası yox idi.

Addım 4. Təzə sındırılmış təsadüfi nömrədən istifadə edərək ilkin dəyəri bərpa edin

Yuxarıda gördüyümüz kimi, SHA512-dən mt_rand() tərəfindən yaradılan istənilən dəyəri çıxarmaq cəmi bir neçə dəqiqə çəkir. Təsadüfi bir dəyərlə silahlanmış, başqa bir kobud güc alətini işlədə bilərik - php_mt_seed. Bu kiçik yardım proqramı mt_rand() nəticəsini götürür və kobud qüvvədən sonra analizin yaradıla biləcəyi ilkin dəyəri hesablayır. Cari versiyanı yükləyin, tərtib edin və işə salın. Əgər kompilyasiya ilə bağlı probleminiz varsa, köhnə versiyanı sınayın (yeniləri ilə virtual mühitlərdə problemlərim var idi).


./php_mt_seed

Bu, CPU-da edildiyi üçün SHA512 krekinqindən bir az daha uzun çəkə bilər. Layiqli bir prosessorda, yardım proqramı bir neçə dəqiqə ərzində ilkin dəyərin bütün mümkün diapazonunu tapacaqdır. Nəticə bir və ya bir neçə mümkün dəyərdir (yəni təsadüfi ədədi yaratmaq üçün istifadə edilə bilən dəyərlər). Yenə zəif entropiyanın nəticəsini yalnız bu dəfə PHP-nin Mersenne burulğan funksiyası üçün ilkin dəyərlər yaratması ilə bağlı görürük. Bu dəyərlərin daha sonra necə yaradıldığına baxacağıq, beləliklə, kobud gücün niyə bu qədər tez həyata keçirilə biləcəyini görəcəksiniz.


Beləliklə, bundan əvvəl internetdə mövcud olan sadə hack alətlərindən istifadə edirdik. Onlar mt_rand() zəngləri üçün nəzərdə tutulub, lakin digər ssenarilərə tətbiq oluna bilən ideyanı təsvir edir (məsələn, tokenlər yaradan zaman ardıcıl mt_rand() zəngləri). Həm də unutmayın ki, sındırma sürəti spesifik token yaratma yanaşmalarını nəzərə alan göy qurşağı cədvəllərinin yaradılmasına mane olmur. Budur mt_rand() zəifliklərindən istifadə edən və Python-da yazılmış başqa bir alət.

Addım 5. Mümkün administrator hesabı parol sıfırlama nişanlarını yaradın

Fərz edək ki, A və B sorğuları daxilində mt_rand() -a yalnız iki sorğu edilib. İndi əvvəllər hesablanmış mümkün ilkin dəyərlərdən istifadə edərək tokenləri proqnozlaşdırmağa başlayaq:


funksiyası proqnozlaşdırın($seed) ( /** * İlkin dəyəri PRNG-ə ötürün */ mt_srand($seed); /** * A sorğusundan funksiya çağırışını keçin */ mt_rand(); /** * Proqnoz edin və qaytarın sorğuda yaradılan B token */ $token = hash("sha512", mt_rand() $token;

Bu funksiya hər mümkün toxum üçün sıfırlama işarəsini proqnozlaşdırır.

Addım 6 və 7: Admin hesabınızın parolunu sıfırlayın və əylənin!

İndi siz proqram zəifliyinə görə administrator parolunu sıfırlamağa və hesabınıza giriş əldə etməyə imkan verəcək işarədən ibarət URL toplamalısınız. Siz forumda və ya məqalədə süzülməmiş HTML yerləşdirə biləcəyinizi aşkar edə bilərsiniz (dərinlikdə müdafiə prinsipinin ümumi pozulması). Bu, proqramın bütün digər istifadəçilərinə onların kompüterlərini zərərli proqram və Man-In-The-Browser monitorinqi ilə yoluxduraraq geniş XSS hücumu həyata keçirməyə imkan verəcək. Ciddi, niyə yalnız giriş əldə etməyi dayandırırsınız? Pasif görünən və çox təhlükəli olmayan zəifliklərin məqsədi təcavüzkarın nəhayət əsas məqsədinə çata biləcəyi yerə yavaş-yavaş nüfuz etməsinə kömək etməkdir. Hacking, bir sıra güclü hücumları həyata keçirmək üçün düzgün kombinasiyanı tez basmalı olduğunuz arcade döyüş oyunu oynamağa bənzəyir.

Hücumdan sonrakı təhlil

Yuxarıdakı ssenari və addımların sadəliyi sizə mt_rand() təhlükələrini aydın şəkildə nümayiş etdirməlidir. Risklər o qədər göz qabağındadır ki, indi biz hər hansı formada təcavüzkar üçün əlçatan olan zəif gizli mt_rand() çıxış dəyərlərini “məlumatların açıqlanması” zəifliyi hesab edə bilərik.


Üstəlik, bu hekayənin ikinci tərəfi də var. Məsələn, bəzi vacib tapşırıqlar üçün mt_rand()-dan məsum şəkildə istifadə edən kitabxanadan asılısınızsa, hətta nəticədə çıxan dəyərləri vermədən belə, ehtiyaclarınız üçün “sızan” işarədən istifadə etməklə, bu kitabxananı güzəştə gedəcəksiniz. Və bu problemdir, çünki kitabxana və ya çərçivə ilkin dəyərin bərpası hücumunu azaltmaq üçün heç bir iş görmür. İstifadəçini mt_rand() dəyərlərini sızdırmaqda günahlandırmalıyıq, yoxsa daha yaxşı təsadüfi dəyərlər tətbiq etmədiyi üçün kitabxananı?


Əslində hər ikisi kifayət qədər günahkardır. Kitabxana təsadüfi dəyərlərin yeganə mənbəyi kimi mühüm problemlər üçün mt_rand() (və ya zəif entropiyanın hər hansı digər tək mənbəyini) seçməməlidir. Və istifadəçi mt_rand() dəyərlərini sızdıran kod yazmamalıdır. Beləliklə, bəli, mt_rand() istifadəsinin savadsız nümunələrinə ittiham barmağını göstərməyə başlaya bilərsiniz, hətta bu birbaşa sızmalara səbəb olmasa belə.


Narahat olmanız lazım olan təkcə məlumatın açıqlanması ilə bağlı zəifliklər deyil. Texniki cəhətdən kriptoqrafik olmayan, lakin qeyri-trivial tətbiq funksiyalarının işində istifadə olunan həssas tokenlərin, açarların və ya qeyri-noncelərin kobud gücünə tətbiqləri həssas qoyan entropiya zəifliklərinin olmamasından xəbərdar olmaq da vacibdir.

Və indi hər şey eynidir

İndi bilirik ki, PHP-də quraşdırılmış PRNG-lərdən istifadə entropiya zəifliyinin olmaması hesab olunur (yəni qeyri-müəyyənliyin azaldılması kobud gücü asanlaşdırır). Hücumumuzu genişləndirə bilərik:


Məlumatın açıqlanması zəifliyi bu token yaratma metodunu tamamilə yararsız edir. Səbəbini anlamaq üçün gəlin PHP funksiyasına yaxından nəzər salaq uniqid() . Onun tərifi:


Mikrosaniyələrdə cari vaxta əsaslanaraq, unikal prefiks identifikatoru əldə edir.


Xatırladığınız kimi, entropiya qeyri-müəyyənliyin ölçüsüdür. Məlumatın açıqlanması zəifliyinə görə, mt_rand() tərəfindən yaradılan dəyərlər sızdırıla bilər, buna görə də mt_rand()-dan unikal identifikator prefiksi kimi istifadə sıfır qeyri-müəyyənlik əlavə edir. Bizim nümunəmizdə uniqid()-ə daxil olan yeganə başqa növ vaxtdır. Amma mütləq qeyri-müəyyən DEYİL. Xətti və proqnozlaşdırıla bilən şəkildə dəyişir. Və proqnozlaşdırıla bilən dəyərlər son dərəcə aşağı entropiyaya malikdir.


Əlbəttə ki, tərif "mikrosaniyələrə", yəni saniyənin milyonda bir hissəsinə aiddir. Bu bizə 1.000.000 mümkün rəqəm verir. Burada 1 saniyədən çox olan dəyərlərə məhəl qoymuram, çünki onların fraksiyaları və ölçülməsi o qədər böyükdür (məsələn, cavabda HTTP Tarixi başlığı), demək olar ki, heç nə vermir. Təfərrüata varmazdan əvvəl uniqid() funksiyasını tədqiq edək və onun C koduna baxaq:


gettimeofday((struct timeval *) &tv, (struct time zone *) NULL); san = (int) tv.tv_sec; usec = (int) (tv.tv_usec % 0x100000); /* usec maksimum 0xF423F dəyərinə malik ola bilər, ona görə də biz * yalnız beş onaltılıq rəqəmdən istifadə edirik. */ if (daha_entropiya) ( spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefiks, sec, usec, php_combined_lcg(TSRMLS_C) * 10); ) else ( spprintf(&uniqid, 0, "% s%08x%05x", prefiks, sec, usec); ) RETURN_STRING(uniqid, 0);

Bu çox mürəkkəb görünürsə, hər şeyi köhnə PHP-də təkrarlaya bilərsiniz:


funksiya unikal_id($prefiks = "", $daha_entropiya = false) ( siyahı($usec, $san) = partlatmaq(" ", microtime()); $usec *= 1000000; if(true === $more_entropy) ( return sprintf("%s%08x%05x%.8F", $prefiks, $sec, $usec, lcg_value()*10 else ( return sprintf("%s%08x%05x", $prefiks, $ san); , $usec);

Bu kod bizə deyir ki, sadəcə uniqid() parametrləri olmadan çağırmaq bizə 13 simvollu sətir qaytaracaq. İlk 8 simvol onaltılıq rəqəmlə ifadə olunan cari Unix vaxt damğasıdır (saniyələrlə). Son 5 simvol onaltılıq sistemdə əlavə mikrosaniyədir. Başqa sözlə, əsas uniqid() funksiyası sistem vaxtının çox dəqiq ölçülməsini təmin edir ki, bu da belə kodla uniqid()-ə sadə çağırışdan çıxarıla bilər:


$id = uniqid(); $zaman = str_split($id, 8); $san = hexdec("0x" . $vaxt); $usec = hexdec("0x" . $vaxt); echo "Saniyələr: ", $san, PHP_EOL, "Mikrosaniyələr: ", $usec, PHP_EOL;

C koduna baxın. Dəqiq sistem vaxtı parametrlərdən asılı olmayaraq heç vaxt çıxışda gizlənmir:


echo uniqid(), PHP_EOL; // 514ee7f81c4b8 echo uniqid("prefiks-"), PHP_EOL; // prefiks-514ee7f81c746 echo uniqid("prefiks-", doğru), PHP_EOL; // prefiks-514ee7f81c8993.39593322

Brute force unikal identifikatorları

Düşündükdən sonra məlum olur ki, təcavüzkar üçün hər hansı uniqid() dəyərinin aşkar edilməsi potensial məlumatın açıqlanması zəifliyinin başqa bir nümunəsidir. Bu, sonrakı uniqid() zəngləri üçün girişi proqnozlaşdırmaq üçün istifadə edilə bilən çox dəqiq sistem vaxtını sızdırır. Bu, 1.000.000 imkanları daha dar diapazona daraltmaqla mikrosaniyələri proqnozlaşdırmağa çalışarkən yaranan dilemmaları həll etməyə kömək edir. Bu sızma daha sonra qeyd edilə bildiyi üçün bizim nümunəmizdə texniki olaraq buna ehtiyac yoxdur. Orijinal uniqid() token koduna yenidən baxaq:


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

Bu misaldan görə bilərik ki, uniqid()-dən məlumatın açıqlanması ilə birlikdə mt_rand() üzərinə sıfırlama hücumu həyata keçirməklə biz nisbətən kiçik SHA512 hash dəstini hesablayacağıq ki, bu da parol sıfırlamaları və ya digər vacib əlamətlər ola bilər. Uniqid() -dən sistem vaxtının sızmasından istifadə etmədən dar diapazonlu vaxt ştamplarına ehtiyacınız varsa, o zaman adətən HTTP Tarix başlığını ehtiva edən server cavablarını təhlil edək. Buradan dəqiq server vaxt damğalarını əldə edə bilərsiniz. Və bu halda entropiya bir milyon mümkün mikrosaniyə dəyərinə bərabər olduğundan, onu bir neçə saniyə ərzində bruteforce edə bilərsiniz!


Artan entropiya bizi xilas edəcəkmi?

Təbii ki, funksiyanın ikinci parametrini TRUE olaraq təyin etməklə uniqid()-ə entropiya əlavə etmək mümkündür:


C kodundan göründüyü kimi, yeni entropiya mənbəyi daxili php_combined_lcg() funksiyasının çıxışından istifadə edir. Bu funksiya PHP-də uniqid() funksiyasının çevrilməsində istifadə etdiyim lcg_value() funksiyası vasitəsilə istifadəçi sahəsinə məruz qalır. Əsasən, ayrı-ayrı ilkin dəyərlər verilmiş iki xətti konqruent generator tərəfindən yaradılan iki dəyəri birləşdirir. Aşağıda generatorları bu ilkin dəyərlərlə təmin edən kod verilmişdir. mt_rand() ilə olduğu kimi, onlar hər PHP prosesində bir dəfə yaradılır və bütün sonrakı zənglərdə təkrar istifadə olunur.


statik boşluq 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; }

Əgər ona çox uzun müddət baxsanız və monitora bir şey atmaq istəyirsinizsə, etməmək daha yaxşıdır. Bu gün monitorlar bahadır.


Hər iki ilkin dəyər Unix Epoch-dan (server saatına istinad edərək) cari vaxtı saniyə və mikrosaniyələrlə çəkmək üçün C gettimeofday() funksiyasından istifadə edir. Qeyd etmək lazımdır ki, hər iki çağırış mənbə kodunda həyata keçirilir, ona görə də onların arasında microsecond() sayğacının dəyəri minimal olacaq, bu da daxil edilmiş qeyri-müəyyənliyi azaldır. İkinci ilkin dəyər həm də Linux-da əksər hallarda 32,768-i keçməyəcək cari prosesin identifikatoruna qarışdırılacaq. , lakin bu çox arzuolunmazdır.


Məlum olub ki, bu LCG-lərin istifadə etdiyi entropiyanın əsas mənbəyi mikrosaniyələrdir. Məsələn, mt_rand() ilkin dəyərimizi xatırlayırsınız? Bunun necə hesablandığını təxmin edin.


#ifdef PHP_WIN32 #define GENERATE_SEED() (((uzun) (zaman(0) * GetCurrentProcessId())) ^ ((uzun) (1000000.0 * php_combined_lcg(TSRMLS_C)))) #else #define GENERATE_seED()(() ) (zaman(0) * getpid())) ^ ((uzun) (1000000.0 * php_combined_lcg(TSRMLS_C)))) #endif

Bu o deməkdir ki, PHP-də istifadə olunan bütün ilkin dəyərlər bir-birindən asılıdır. Hətta eyni giriş məlumatları bir neçə dəfə qarışdırılır. Ola bilsin ki, yuxarıda müzakirə etdiyimiz kimi ilkin mikrosaniyələrin diapazonunu məhdudlaşdıra bilərsiniz: iki sorğudan istifadə etməklə, birincisi saniyələr arasında sıçrayışla (beləliklə, mikrotime 0 + növbəti gettimeofday() C çağırışının icra vaxtı olacaq). Əgər mənbə koduna çıxışınız varsa, hətta digər gettimeofday() zəngləri arasında mikrosaniyə deltasını hesablaya bilərsiniz (PHP-nin açıq mənbə təbiəti kömək edir). Nəzərə alsaq ki, mt_rand() ilə toxumu kobud şəkildə zorlamaq sizə oflayn yoxlamaya imkan verən son toxum verir.


Bununla belə, əsas problem php_combined_lcg() -dədir. Bu, PHP prosesində bir dəfə ilkin dəyəri alan lcg_value() funksiyasının aşağı səviyyəli istifadəçi məkanı tətbiqidir. Və bu dəyəri bilirsinizsə, o zaman çıxışı təxmin edə bilərsiniz. Əgər bu qozu sındırsan, deməli, oyun bitdi.

Bunun üçün bir proqram var ...

Mən praktiki şeylərə diqqət yetirmək üçün çox vaxt sərf etmişəm, ona görə də onlara yenidən qayıdaq. php_combined_lcg() tərəfindən istifadə edilən iki ilkin dəyəri əldə etmək o qədər də asan deyil - birbaşa sızma yaratmaq mümkün olmaya bilər. lcg_value() funksiyası nisbətən az məlumdur və proqramçılar PHP-də quraşdırılmış PRNG-ə ehtiyac duyduqda daha çox mt_rand() funksiyasına etibar edirlər. Mən lcg_value() funksiyasının dəyəri sızdırmasının qarşısını almaq istəmirəm, lakin bu, populyar olmayan funksiyadır. Birləşdirilmiş LCG cütü də toxum əkmə funksiyasını əks etdirmir (beləliklə, kiminsə miras kodundan miras qalmış sızan toxumlama mexanizmini müəyyən etmək üçün sadəcə mt_srand()-a zənglər axtara bilməzsiniz). Bununla belə, kobud zorlama toxumları üçün birbaşa məlumat verən etibarlı mənbə var: PHP-də sessiya identifikatorları.


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

Bu kod IP, vaxt damğası, mikrosaniyələr və... php_combined_lcg() çıxışından istifadə edərək sessiya ID-si üçün əvvəlcədən hash dəyəri yaradır. Mikro-müvəqqəti imkanların sayının əhəmiyyətli dərəcədə azaldığını nəzərə alsaq (bu kodun İD-ni yaratmaq üçün 1, php_combined_lcg() üçün isə 2 lazımdır, nəticədə onlar arasında minimal fərq yaranır), biz indi onu kobud şəkildə zorlaya bilərik. Yaxşı, yəqin.


Yadınızdadırsa, PHP indi session.entropy_file və session.entropy_length kimi yeni sessiya seçimlərini dəstəkləyir. Bu, php_combined_lcg() istifadə edərək birləşdirilmiş LCG generatorları üçün tez bir zamanda (saat çəkməyəcək) iki ilkin dəyər əldə edə biləcəyiniz kobud güc sessiya identifikatorlarının qarşısını almaq üçün edilir. Əgər PHP 5.3 və ya daha aşağı versiyadan istifadə edirsinizsə, bu seçimləri düzgün konfiqurasiya etməmiş ola bilərsiniz. Bu o deməkdir ki, LCG üçün ilkin dəyərlər əldə etmək üçün sessiya identifikatorlarını kobud şəkildə tətbiq etməyə imkan verən başqa bir faydalı məlumatın açıqlanması zəifliyi var.


Belə hallar üçün LCG dəyərlərini hesablamağa imkan verən Windows proqramı var.


Yeri gəlmişkən, LCG vəziyyətlərini bilmək mt_rand() funksiyasının ilkin dəyəri necə əldə etdiyini başa düşməyə imkan verir, buna görə də bu, mt_rand() dəyərinin sızması olmamasından xilas olmağın başqa bir yoludur.


Bütün bunlar uniqid()-in qaytarılma dəyərlərinə entropiya əlavə etmək baxımından nə deməkdir?


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

Bu, entropiya çatışmazlığının potensial zəifliyinin başqa bir nümunəsidir. Sızma ilə entropiyaya etibar edə bilməzsiniz (hətta siz onlara cavabdeh deyilsinizsə belə!). Sessiya ID-si haqqında məlumatın sızması sayəsində təcavüzkar bu ID-yə əlavə olaraq əlavə edilmiş entropiya dəyərini də proqnozlaşdıra bilər.


Yenə də kim günahkardır? Əgər X tətbiqi uniqid()-ə əsaslanırsa, lakin eyni serverdəki istifadəçi və ya digər proqram daxili LCG vəziyyətini sızdırırsa, onda siz hər iki vəziyyətdə tədbir görməlisiniz. İstifadəçilər əmin olmalıdırlar ki, sessiya identifikatorları kifayət qədər yüksək entropiyadan istifadə edir və üçüncü tərəf proqramçıları başa düşməlidirlər ki, onların təsadüfi dəyərlər yaratmaq üsullarında entropiya yoxdur, buna görə də daha uyğun alternativlərə keçmək lazımdır (yalnız zəif entropiya mənbələri mövcud olsa belə!) .

Entropiya axtarışında

PHP öz başına güclü entropiya yarada bilməz. Güclü entropiyanın etibarlı mənbələri olan əməliyyat sistemi səviyyəli PRNG generatorlarından məlumatların ötürülməsi üçün əsas API belə yoxdur. Buna görə də isteğe bağlı openssl və mcrypt uzantılarına etibar etməlisiniz. Onlar sızan, proqnozlaşdırıla bilən, aşağı entropiyalı qohumlarından daha yaxşı xüsusiyyətlər təklif edirlər.


Təəssüf ki, hər iki genişləndirmə isteğe bağlı olduğundan, bəzi hallarda son çarə kimi zəif entropiya mənbələrinə etibar etməkdən başqa seçimimiz yoxdur. Bu baş verdikdə, mt_rand()-ın zəif entropiyasını əlavə qeyri-müəyyənlik mənbələri ilə əlavə etmək lazımdır, onların məlumatlarını psevdo-təsadüfi baytların götürülə biləcəyi vahid hovuza qarışdırmaq lazımdır. Güclü entropiya mikserindən istifadə edən oxşar təsadüfi generator artıq Entoni Ferrara tərəfindən RandomLib kitabxanasında həyata keçirilib. Proqramçılar mümkün olduqda bunu etməlidirlər.


Mürəkkəb riyazi çevrilmələri heshing etməklə entropiyanızın zəifliyini gizlətmək istəyindən çəkinin. Təcavüzkar ilkin ilkin dəyəri tapan kimi bütün bunları təkrarlayacaq. Bu cür fəndlər kobud qüvvə zamanı hesablamaların miqdarını yalnız bir qədər artıracaq. Unutmayın: entropiya nə qədər aşağı olarsa, qeyri-müəyyənlik bir o qədər azdır; qeyri-müəyyənlik nə qədər az olarsa, kobud güc tətbiq etmək imkanı bir o qədər az olar. Yeganə əsaslandırılan həll yolu hər hansı bir vasitə ilə istifadə etdiyiniz entropiya hovuzunu artırmaqdır.


RandomLib kitabxanası müxtəlif entropiya mənbələrindən alınan məlumatları qarışdırmaqla və təcavüzkarın təxmin etmək üçün lazım ola biləcəyi məlumatları lokallaşdırmaqla təsadüfi baytlar yaradır. Məsələn, siz mt_rand(), uniqid() və lcg_value() çıxışlarını qarışdıra bilərsiniz, PID, yaddaş istehlakı, bəzi digər mikrotime ölçüləri, $_ENV serializasiya, posix_times() və s. əlavə edə bilərsiniz. Və ya daha da irəli gedə bilərsiniz, bu, RandomLib-ə imkan verir. genişlənmə qabiliyyəti. Deyək ki, biz mikrosaniyələrdə bəzi deltalardan istifadə edirik (yəni, hash() zəngləri kimi psevdo-təsadüfi daxiletmə məlumatları ilə funksiyanın işləməsi üçün neçə mikrosaniyə lazım olduğunu ölçün).

Teqlər əlavə edin
  • Kimdən:
  • Qeydiyyatdan keçib: 2014.07.07
  • Yazılar: 3,775
  • Mən sadəcə PunBB-ni bəyənirəm:
  • 5 illər, 6 aylar,
  • Bəyənmələr: 463

Mövzu: PHP-də massivdən təsadüfi dəyəri necə seçmək olar

Bu funksiyadan istifadə edərək massivin təsadüfi elementini (və ya elementlərini) seçə bilərik. Bəli, məhz element və ya elementlər! Bu bir element ola bilər və ya bir neçə element ola bilər. Hamısı qarşılaşdığınız vəzifədən asılıdır.

Bununla belə, burada nəzərə almaq lazımdır ki, funksiya elementin dəyərini deyil, onun açarını (və ya bir neçə element varsa, düymələri) qaytaracaqdır.

Funksiya mötərizədə parametrlər kimi qəbul edilir: işlədiyimiz massivin adı və seçilməli olan elementlərin sayı.

Ümumiyyətlə, hər şey sadədir! Bütün bunları misallarla nəzərdən keçirsək daha asan olacaq.

Əvvəlcə massivdən bir təsadüfi element seçək.

2 Cavab verin PunBB

  • Kimdən: Moskva, Sovxoznay 3, mənzil. 98
  • Qeydiyyatdan keçib: 2014.07.07
  • Yazılar: 3,775
  • Mən sadəcə PunBB-ni bəyənirəm:
  • 5 illər, 6 aylar,
  • Bəyənmələr: 463

Təsəvvür edək ki, veb saytımızın yuxarı hissəsində bir neçə sitat göstərmək istəyirik. Təbii ki, sitatlar dəyişməlidir. Bir istifadəçi hər dəfə saytınıza daxil olduqda, istifadəçinin yeni sitat görməsini istəyirsiniz.

Yəqin ki, təxmin etdiyiniz kimi, bunu həyata keçirməyin ən asan yolu bütün mövcud sitatları və deyimləri massivdə yerləşdirmək və sonra bu massivdən təsadüfi elementi seçmək və onu ekranda göstərməkdir.

Massivdə nə qədər çox sitat varsa, onların təkrarlanma ehtimalı bir o qədər az olar.

Ancaq misal üçün, çox narahat olmayacağam və serialıma 7 deyim yerləşdirəcəyəm.

Sonra mən array_rand() funksiyasının nəticəsini saxlayacağam dəyişən yaratmalı olacağam. Mötərizədə bu funksiyanın iki arqumenti olacaq: massivimizin adı və bizə lazım olan təsadüfi elementlərin sayı.

Artıq dediyim kimi, funksiya elementin dəyərini deyil, onun açarını (və ya siyahıdakı nömrəni) qaytarır. Beləliklə, təsadüfi elementin açarı dəyişəndə ​​saxlanılacaq.

Bundan sonra sadəcə istədiyiniz elementin dəyərini göstərməli olacağam. Bunun üçün massivin adını və kvadrat mötərizədə təsadüfi açarı ehtiva edən dəyişənimizin adını qeyd edirəm.

Hamısı budur. Aşağıdakı koda baxın və düşünürəm ki, hər şeyi tam başa düşəcəksiniz:

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

3 Cavab verin PunBB

  • Kimdən: Moskva, Sovxoznay 3, mənzil. 98
  • Qeydiyyatdan keçib: 2014.07.07
  • Yazılar: 3,775
  • Mən sadəcə PunBB-ni bəyənirəm:
  • 5 illər, 6 aylar,
  • Bəyənmələr: 463

Re: PHP-də massivdən təsadüfi dəyəri necə seçmək olar

İndi bir neçə təsadüfi massiv elementini çap etməyə məşq edək.

Bir element halında onun açarı, massivin bir neçə təsadüfi elementi olduqda isə düymələr massivi qaytarılır. Ekranda göstərərkən bundan başlayacağıq.

Əvvəlcə 7 fərqli ad əlavə edəcəyimiz massiv yaradaq.

Sonra, array_rand() funksiyasının işinin qeyd olunacağı dəyişən yaradırıq. Yalnız indi bu funksiya üçün mötərizədə ikinci arqument kimi “2” rəqəmini göstəririk. Bu o deməkdir ki, bizə 2 təsadüfi element lazımdır.

Bu vəziyyətdə funksiyanın nəticəsi əsas massivimizdən elementlərin iki təsadüfi açarını ehtiva edən massiv olacaqdır.

Buna görə də ekranda göstərərkən bunu nəzərə almalı və kvadrat mötərizədə sadəcə dəyişənin adını deyil, dəyişənin adını, sonra kvadrat mötərizəni və massiv indeksini göstərməlisiniz. 2 elementimiz olduğundan birinci halda indeks , ikincidə isə . (Massivlərdə indeksləşdirmənin "0"dan başladığını xatırlayırsınız.)

Hamısı budur. Hər şeyi daha aydın etmək üçün koda nəzər salın:

$adlar = massiv("Maşa", "Saşa", "Nadya", "Mila", "Andrey", "Sergey", "Anton"); $rand_adları = massiv_rand($adlar,2); əks-səda "

".$names[$rand_names]." və ".$names[$rand_names]."

";

Nəticədə ekranda iki təsadüfi ad görünəcək. Səhifə hər dəfə yenilənəndə adlar dəyişəcək.

Mənbə



Əlaqədar nəşrlər