• Php rastgele değeri. PHP'de rastgele sayılar üretme hakkında birkaç söz. Rastgele Sayıları Kullanma

    rand(1,N) ancak dizi(a,b,c,..) hariç

    Zaten bilmediğim yerleşik bir işlev var mı veya onu kendim uygulamam gerekiyor mu (nasıl?)?

    GÜNCELLEME

    Hariç tutulan dizinin boyutu ne kadar büyük veya küçük olursa olsun, nitelikli bir çözümün altın içermesi gerekir.

    Yerleşik bir işlev yok, ancak siz yapabilirsiniz yap:

    Function randWithout($from, $to, dizi $istisnalar) ( sort($istisnalar); // foreach'ta break;'ı güvenilir bir şekilde kullanmamıza izin verir $number = rand($from, $to - count($istisnalar)); / / veya mt_Rand() foreach ($istisnalar as $istisna) ( if ($sayı >= $istisna) ( $sayı++; // boşluğu telafi edin ) else /*if ($sayı)< $exception)*/ { break; } } return $number; }

    Kafamda değil, bu yüzden cilalamak gerekebilir - ama en azından varsayımsal olarak bile sonsuz bir döngü senaryosuna giremezsiniz.

    Not: $istisnalar varsa işlev bozulur egzozlar aralığınız - örneğin randWithout(1, 2, array(1,2)) veya randWithout(1, 2, array(0,1,2,3)) çağrılması makul bir şey vermeyecektir (açıkçası), ancak bu durumda döndürülen sayı $başlangıç ​​– $başlangıç ​​aralığının dışında olacaktır, dolayısıyla yakalanması kolaydır.

    Eğer $istisnaların zaten sıralanacağı garanti ediliyorsa, sort($istisnalar); silinebilir.

    Göz şekeri: Algoritmanın minimal düzeyde görselleştirilmesi.

    Böyle yerleşik bir işlevin olduğunu sanmıyorum; muhtemelen bunu kendiniz kodlamanız gerekecektir.

    Bunu kodlamak için iki çözümünüz var:

    • Doğru değeri döndürene kadar Rand() veya mt_Rand()'ı çağırmak için bir döngü kullanın
      • bu, Rand() işlevini birden çok kez çağırmak anlamına gelir; en kötü durum senaryosu
      • ancak N büyükse ve çok fazla yasaklı değeriniz yoksa bu işe yarayacaktır.
    • Yalnızca yasal değerleri içeren bir dizi oluşturun
      • Ve kullan dizi_rand ondan bir değer seçmek için
      • N küçükse bu işe yarayacaktır

    Neye ve neden ihtiyacınız olduğuna bağlı olarak bu yaklaşım ilginç bir alternatif olabilir.

    $sayılar = array_diff(aralık(1, N), dizi(a, b, c)); // İkisi de (gerçek bir cevap değil ama koşullarınıza bağlı olarak yararlı olabilir) shuffle($numbers); // $sayılar artık ilginizi çeken tüm sayıları içeren rastgele sıralanmış bir dizidir // Veya: $x = $sayılar; // $x artık ilgilendiğiniz sayılar kümesinden seçilen rastgele bir sayıdır

    Dolayısıyla, her seferinde bir dizi potansiyel sayı oluşturmanız gerekmiyorsa, ancak bir kümeyi bir kez oluşturup ardından aynı kümeden bir grup rastgele sayı seçiyorsanız, bu iyi bir yol olabilir.

    En kolay yol …

    M = N - istisna sayısı uzunluğundaki bitişik bir dizide rastgele bir konum seçebilmeniz ve bunu deliklerle birlikte orijinal diziye kolayca geri eşleyebilmeniz için bir dizi eksik konum hesaplamanız gerekir. Bu, geçirilen diziye eşit zaman ve alan gerektirecektir. PHP'yi yerdeki bir delikten bilmiyorum, bu yüzden yarı psudo metin örnek kodunu affedin.

    1. İstisnalar dizisiyle aynı uzunlukta yeni bir Ofset dizisi yapın.
    2. Ofset[i]'de ilk dizini hayali, boş olmayan bir dizide saklar, bu da orijinal dizideki i öğelerini kaçırır.
    3. Şimdi rastgele bir öğe seçin. Kalan elemanların sayısını 0..M'ye kadar rastgele bir sayı seçin, r.
    4. Bu Ofset'i bul[i]<= r < Offest это легко с бинарным поиском
    5. r+i'yi döndür

    Şimdi, bu sadece bir taslak, dizilerin sonlarıyla ve eğer bir şey 0 veya 1 şeklinde indekslenmişse ve tüm bu cazlarla uğraşmanız gerekecek. Eğer akıllıysanız, aslında Ofset dizisini orijinalden anında hesaplayabilirsiniz, ancak bu biraz daha az açıktır.

    Belki bir cevap için çok geç, ancak bir sayı dışında rastgele bir kimliğe dayalı bir veritabanından rastgele veri almaya çalışırken bu kod parçasını aklımda bir yerde buldum.

    $hariç tutulanVeri = dizi(); // Bu sizin hariç tuttuğunuz numaradır $maxVal = $this->db->count_all_results("game_pertanyaan"); // Veritabanıma göre maksimum sayıyı al $randomNum = Rand(1, $maxVal); // İlk başlatmayı yapın, sanırım bunu doğrudan while > in_array parametresine koyabilirsiniz, o da çalışıyor gibi görünüyor, bu size kalmış while (in_array($randomNum, $excludedData)) ( $randomNum = rand(1, $maxVal); ) $randomNum; //Seçtiğiniz bazı sayılar dışındaki rastgele sayınız

    öğe seç (11)

    $ran = array(1,2,3,4); adında bir dizim var.

    Bu diziden rastgele bir değer alıp bunu bir değişkende saklamam gerekiyor, bunu nasıl yapabilirim?

    Yanıtlar

    Cevabımı @OlafurWaage'ın işlevine dayandırıyorum. Kullanmaya çalıştım ancak döndürülen nesneyi değiştirmeye çalıştığımda yardım sorunlarıyla karşılaştım. Referans olarak iletmek ve geri dönmek için işlevini güncelledim. Yeni özellik:

    Function &random_value(&$dizi, $default=null) ( $k = mt_rand(0, count($dizi) - 1); if (isset($dizi[$k])) ( return $dizi[$k]; ) else ( $varsayılanı döndür; ))

    Daha fazla bilgi için Nesne Referanslarını İletme/Geri Verme + Nesneyi Değiştirme Çalışmıyor bölümündeki sorumu inceleyin.

    $değer = $dizi;

    Aşağıda gösterildiği gibi dizinizden rastgele bir anahtar seçmek için array_Rand işlevini kullanabilirsiniz.

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

    veya rastgele bir dizin seçmek için Rand ve Count işlevlerini kullanabilirsiniz.

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

    $rastgele = $randı;

    Bir değere ihtiyacınız varsa, bu aynı zamanda bir işlev olarak da kullanışlıdır.

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

    Seçiminizin herhangi bir güvenlik etkisi var mı? Eğer öyleyse, random_int() ve array_keys() kullanın. (random_bytes() yalnızca PHP 7'dir, ancak PHP 5 için çoklu doldurma vardır).

    Function random_index(array $source) ( $max = count($source) - 1; $r = random_int(0, $max); $k = array_keys($source); return $k[$r]; )

    Başvuru:

    $dizi = [ "elma" => 1234, "oğlan" => 2345, "kedi" => 3456, "köpek" => 4567, "yankı" => 5678, "talih" => 6789 ]; $i = random_index($dizi); var_dump([$i, $dizi[$i]]);

    Function array_random($dizi, $tutar = 1) ( $anahtarlar = array_rand($dizi, $tutar); if ($tutar == 1) ( return $dizi[$anahtarlar]; ) $sonuçlar = ; foreach ($anahtarlar) $anahtar olarak) ( $sonuçlar = $dizi[$anahtar]; ) $sonuçları döndürür; )

    Başvuru:

    $items = ["foo", "bar", "baz", "lorem"=>"ipsum"]; array_random($öğeler); // "bar" array_random($öğeler, 2); // ["foo", "ipsum"]

    Birkaç not:

    • $amount, count($array) değerinden küçük veya ona eşit olmalıdır.
    • array_Rand() işlevi anahtarları karıştırmaz (PHP 5.2.10'dan beri, bkz.), dolayısıyla seçtiğiniz öğeler her zaman orijinal sıralarında olacaktır. Gerekirse shuffle() işlevini kullanın.

    düzenlemek: Laravel işlevi o zamandan bu yana önemli ölçüde büyüdü, bkz. Laravel 5.4"ün Arr::random()'u. Burada olgun bir Laravel işlevinden kaynaklanan daha karmaşık bir şey var:

    Function array_random($array, $number = null) ( $requested = ($number === null) ? 1: $number; $count = count($array); if ($requested > $count) ( throw new \ RangeException("($requested) öğe istediniz, ancak yalnızca ($count) öğe mevcut."); ) if ($number === null) ( return $array; ) if ((int) $number == = 0) ( return ; ) $anahtarlar = (dizi) array_rand($dizi, $sayı); $sonuçlar = ; foreach ($anahtar olarak $anahtarlar) ( $sonuçlar = $dizi[$anahtar]; ) return $sonuçlar; )

    Birkaç önemli nokta:

    • Yeterli öğe yoksa bir istisna atın
    • array_random($array, 1) bir öğeden oluşan bir diziyi döndürür (#19826)
    • Öğe sayısı için "0" değerini destekleyin (Bu sadece temiz ve basittir.

    Rastgele değerler PHP'nin her yerindedir. Tüm çerçevelerde, birçok kütüphanede. Muhtemelen jetonlar ve tuzlar oluşturmak için ve işlevlere girdi olarak rastgele değerler kullanan bir sürü kodu kendiniz yazmışsınızdır. Ayrıca rastgele değerler çeşitli problemlerin çözümünde önemli bir rol oynar:

    1. Bir havuzdan veya bilinen seçenekler aralığından seçenekleri rastgele seçmek için.
    2. Şifreleme için başlatma vektörleri oluşturmak.
    3. Yetkilendirme sırasında öngörülemeyen belirteçler veya tek seferlik değerler oluşturmak.
    4. Oturum kimlikleri gibi benzersiz tanımlayıcılar oluşturmak.

    Tüm bu durumlarda karakteristik bir güvenlik açığı vardır. Bir saldırgan, Rastgele Sayı Oluşturucunuzun (RNG) veya Sözde Rastgele Sayı Oluşturucunuzun (PRNG) çıktısını tahmin ederse veya tahmin ederse, bu oluşturucu tarafından oluşturulan jetonları, tuzları, nonce'leri ve kriptografik başlatma vektörlerini hesaplayabilecektir. Bu nedenle, yüksek kaliteli rastgele değerler, yani tahmin edilmesi son derece zor olan değerler üretmek çok önemlidir. Hiçbir zaman parola sıfırlama belirteçlerini, CSRF belirteçlerini, API anahtarlarını, nonce'ları veya yetkilendirme belirteçlerini öngörülebilir hale getirmeyin!


    PHP'de rastgele değerlerle ilişkili iki potansiyel güvenlik açığı daha vardır:

    1. Bilgi İfşası.
    2. Yetersiz Entropi.

    Bu bağlamda "bilginin ifşa edilmesi", bir sözde rastgele sayı üretecinin iç durumunun, yani çekirdek değerinin sızdırılması anlamına gelir. Bunun gibi sızıntılar gelecekteki PRNG çıktılarını tahmin etmeyi çok daha kolay hale getirebilir.


    "Entropi eksikliği", bir PRNG'nin başlangıçtaki iç durumunun (tohumunun) veya çıktısının değişkenliğinin o kadar küçük olduğu ve olası değerlerin tüm aralığının kaba kuvvetle nispeten kolay olduğu bir durumu tanımlar. PHP programcıları için pek de iyi bir haber değil.


    Örnek saldırı senaryoları ile her iki zafiyeti detaylı olarak inceleyeceğiz. Ama önce PHP programlamada rastgele değerin gerçekte ne olduğunu anlayalım.

    Rastgele değerler ne işe yarar?

    Rastgele değişkenlerin amacı hakkındaki kafa karışıklığı, genel bir yanlış anlama ile daha da artmaktadır. Şüphesiz, kriptografik olarak güçlü rastgele değerler ile "diğer kullanımlar için" belirsiz "benzersiz" değerler arasındaki farkı duymuşsunuzdur. Ana izlenim, kriptografide kullanılan rastgele değerlerin yüksek kalitede rastgelelik (veya daha doğrusu yüksek entropi) gerektirmesi, diğer uygulamalara yönelik değerlerin ise daha az entropi ile idare edebilmesidir. Bu izlenimi yanlış ve verimsiz buluyorum. Tahmin edilemeyen rastgele değerler ile önemsiz görevler için gerekli olanlar arasındaki gerçek fark, ikincisinin öngörülebilirliğinin zararlı sonuçlara yol açmamasıdır. Bu, kriptografiyi tamamen değerlendirme dışı bırakır. Başka bir deyişle, önemsiz olmayan bir problemde rastgele bir değer kullanırsanız, otomatik olarak çok daha güçlü RNG'leri seçmelisiniz.


    Rastgele değerlerin gücü, onları üretmek için harcanan entropi ile belirlenir. Entropi, "bit" cinsinden ifade edilen belirsizliğin bir ölçüsüdür. Örneğin, bir ikili bit alırsam değeri 0 veya 1 olabilir. Saldırgan tam değeri bilmiyorsa, o zaman 2 bitlik bir entropimiz olur (yani yazı tura atışı). Saldırgan değerin her zaman 1 olduğunu biliyorsa, o zaman 0 bitlik bir entropiye sahip oluruz, çünkü öngörülebilirlik belirsizliğin zıt anlamlısıdır. Ayrıca bit sayısı 0 ila 2 arasında değişebilir. Örneğin, bir ikili bitin %99'u 1 ise, bu durumda entropi 0'dan biraz daha büyük olabilir. Yani ne kadar tanımsız ikili bit seçersek o kadar iyidir.


    PHP'de bu daha net görülebilir. mt_Rand() işlevi rastgele değerler üretir; bunlar her zaman sayılardır. Harfleri, özel karakterleri veya başka anlamları göstermez. Bu, saldırganın her bayt için çok daha az tahmine sahip olduğu, yani düşük entropiye sahip olduğu anlamına gelir. Eğer mt_Rand() işlevini Linux kaynağından /dev/random bayt okuyarak değiştirirsek, gerçekten rastgele baytlar elde ederiz: bunlar, sistem aygıt sürücüleri ve diğer kaynaklar tarafından üretilen gürültüye dayalı olarak oluşturulur. Açıkçası, bu seçenek çok daha iyidir çünkü önemli ölçüde daha fazla entropi biti sağlar.


    mt_Rand()'ın istenmeyen durumu, onun gerçekten rastgele değil, sözde rastgele sayılar üreteci olması veya aynı zamanda adlandırıldığı gibi, rastgele ikili dizilerin deterministik bir üreteci olması gerçeğiyle de gösterilir (Deterministic Random Bit Generator, DRBG). ). Sonuç gerçek bir rastgele sayı üretecinin sonucuna yakın olacak şekilde dağıtılmış sayılar üreten Mersenne Twister adı verilen bir algoritma uygular. mt_Rand() yalnızca tek bir rastgele değer kullanır - tohum; buna dayalı olarak sabit bir algoritma sözde rastgele değerler üretir.


    Bu örneğe bir göz atın, kendiniz test edebilirsiniz:


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

    Bu, PHP Mersenne girdap işlevine önceden belirlenmiş bir başlangıç ​​değeri verildikten sonra yürütülen basit bir döngüdür. Mt_srand() dokümantasyonunda örnek olarak verilen fonksiyonun çıktısından ve geçerli saniye ve mikrosaniye kullanılarak elde edilmiştir. Yukarıdaki kodu çalıştırırsanız, 25 sözde rastgele sayı görüntüleyecektir. Rastgele görünüyorlar, tesadüf yok, her şey yolunda. Kodu tekrar çalıştıralım. Bir şey fark ettin mi? Yani: AYNI numaralar görüntüleniyor. Üçüncü, dördüncü, beşinci kez çalıştıralım. PHP'nin eski sürümlerinde sonuç farklı olabilir, ancak PHP'nin tüm modern sürümlerinde ortak olan sorun bu değildir.


    Eğer bir saldırgan böyle bir PRNG'nin çekirdek değerini elde ederse, mt_Rand() çıktısının tamamını tahmin edebilecektir. Bu nedenle başlangıç ​​değerinin korunması son derece önemlidir. Kaybederseniz artık rastgele değerler üretme hakkınız kalmaz...


    Başlangıç ​​değerini iki yoldan biriyle oluşturabilirsiniz:

    • mt_srand() işlevini kullanarak manuel olarak,
    • mt_srand()'ı yok sayacak ve PHP'nin bunu otomatik olarak oluşturmasına izin vereceksiniz.

    İkinci seçenek tercih edilir, ancak bugün bile eski uygulamalar, PHP'nin daha modern sürümlerine geçtikten sonra bile sıklıkla mt_srand() kullanımını devralır.


    Bu, saldırganın gelecekteki değerleri tahmin etmek için yeterli bilgiyi sağlayacak olan başlangıç ​​değerini (Tohum Kurtarma Saldırısı) geri alma riskini artırır. Sonuç olarak, böyle bir sızıntının ardından herhangi bir uygulama, bilgilerin ifşa edilmesine yönelik saldırılara karşı savunmasız hale gelir. Görünüşte pasif doğasına rağmen bu gerçek bir güvenlik açığıdır. Yerel sistemle ilgili bilgilerin sızdırılması, saldırganın daha sonra savunma ilkesini derinlemesine ihlal edecek saldırılarda işine yarayabilir.

    PHP'de rastgele değerler

    PHP üç PRNG kullanır ve eğer bir saldırgan algoritmalarında kullanılan tohumlara erişim kazanırsa çalışmalarının sonuçlarını tahmin edebilecektir:

    1. Doğrusal Uyumlu Jeneratör (LCG), lcg_value() .
    2. Mersenne girdabı, mt_rand() .
    3. Yerel olarak desteklenen C işlevi Rand() .

    Bu oluşturucular ayrıca array_Rand() ve uniqid() gibi işlevler için dahili olarak da kullanılır. Bu, bir saldırganın gerekli tüm tohumları elde etmesi durumunda PHP'nin dahili PRNG'lerini kullanan bu ve diğer işlevlerin çıktısını tahmin edebileceği anlamına gelir. Bu aynı zamanda jeneratörlere birden fazla çağrı yaparak saldırganın kafasını karıştırarak savunmanızı geliştiremeyeceğiniz anlamına da gelir. Bu özellikle açık kaynak uygulamaları için geçerlidir. Saldırgan, bildiği herhangi bir başlangıç ​​değeri için TÜM çıktıları tahmin edebilir.


    Önemsiz olmayan görevler için oluşturulan rastgele değerlerin kalitesini artırmak için PHP, işletim sistemi tarafından sağlanan harici entropi kaynaklarına ihtiyaç duyar. Linux'ta genellikle /dev/urandom kullanılır, doğrudan okuyabilir veya openssl_pseudo_random_bytes() veya mcrypt_create_iv() işlevlerini kullanarak dolaylı olarak erişebilirsiniz. Her ikisi de Windows'ta kriptografik olarak güvenli bir sözde rastgele sayı üreteci (CSPRNG) kullanabilir, ancak PHP'de kullanıcı alanında bu işlevler tarafından sağlanan uzantılar olmadan bu oluşturucudan veri almak için henüz doğrudan bir yöntem yoktur. Başka bir deyişle, PHP sunucu sürümünüzün OpenSSL veya Mcrypt uzantısının etkin olduğundan emin olun.


    /dev/urandom bir PRNG'dir, ancak sıklıkla yeni tohumları yüksek entropili kaynak /dev/random'dan alır. Bu da onu bir saldırgan için ilginç olmayan bir hedef haline getiriyor. Engelleyici bir kaynak olduğundan doğrudan /dev/random'dan okumaktan kaçınmaya çalışıyoruz. Entropinin tükenmesi durumunda, sistem ortamından tekrar yeterli entropi elde edilene kadar tüm okumalar engellenecektir. En önemli görevler için /dev/random kullanmalısınız.


    Bütün bunlar bizi şu kurala götürüyor:


    Önemsiz olmayan rastgele sayıların kullanımını içeren tüm işlemler, openssl_pseudo_random_bytes() işlevini kullanmalıdır ZORUNLU. Alternatif olarak baytları doğrudan /dev/urandom'dan okumayı deneyebilirsiniz. Her iki seçenek de işe yaramazsa ve başka seçeneğiniz yoksa, o zaman değeri, birden fazla rastgele veya gizli değer kaynağından gelen verileri yoğun bir şekilde karıştırarak oluşturmanız GEREKİR.

    Bu kuralın temel uygulamasını SecurityMultiTool referans kitaplığında bulacaksınız. Her zaman olduğu gibi, PHP'nin içindekiler doğrudan PHP çekirdeğine güvenli çözümler eklemek yerine programcıların hayatını zorlaştırmayı tercih ediyor.


    Bu kadar teori yeter, şimdi yukarıdakilerle donatılmış bir uygulamaya nasıl saldırabileceğinizi görelim.

    PHP'de rastgele sayı üreteçlerine saldırı

    Pek çok nedenden ötürü PHP, önemsiz olmayan sorunları çözmek için PRNG'leri kullanır.


    openssl_pseudo_random_bytes() işlevi yalnızca PHP 5.3'te mevcuttu. Windows'ta 5.3.4 sürümü çıkana kadar engelleme sorunlarına neden oluyordu. Ayrıca PHP 5.3'te Windows'taki mcrypt_create_iv() işlevi MCRYPT_DEV_URANDOM kaynağını desteklemeye başladı. Daha önce Windows yalnızca MCRYPT_RAND'ı destekliyordu; bu, aslında Rand() işlevi tarafından dahili olarak kullanılan PRNG sisteminin aynısıydı. Gördüğünüz gibi PHP 5.3'ten önce pek çok boşluk vardı, dolayısıyla önceki sürümlerde yazılan birçok eski uygulama daha güçlü PRNG'lere geçiş yapmamış olabilir.


    Openssl ve Mcrypt uzantılarının seçimi sizin takdirinize bağlıdır. PHP 5.3 çalıştıran sunucularda bile kullanılabilir olduklarına güvenilemeyeceğinden, uygulamalar genellikle PHP'de yerleşik PRNG'leri önemsiz olmayan rastgele değerler oluşturmak için bir geri dönüş olarak kullanır.


    Ancak her iki durumda da düşük entropili tohumlara sahip PRNG'ler tarafından oluşturulan rastgele değerleri kullanan önemsiz sorunlarımız var. Bu bizi geri alma saldırılarına karşı savunmasız hale getiriyor. Basit bir örneğe bakalım.


    Uygulama boyunca çeşitli görevlerde kullanılan belirteçleri oluşturmak için aşağıdaki kodu kullanan çevrimiçi bir uygulama bulduğumuzu hayal edelim:


    $belirteç = hash("sha512", mt_rand());

    Token üretmenin daha karmaşık yolları var ama bu iyi bir seçenek. SHA512 ile hashlenmiş mt_Rand() işlevine yalnızca bir çağrı vardır. Uygulamada, eğer bir programcı PHP'nin rastgele değer fonksiyonlarının "yeterince rastgele" olduğuna karar verirse, "kriptografi" kelimesi geçene kadar büyük olasılıkla basit yaklaşımı seçecektir. Örneğin, kriptografik olmayan durumlara erişim belirteçleri, CSRF belirteçleri, API tek seferlik değerler ve parola sıfırlama belirteçleri dahildir. Devam etmeden önce, bu uygulamanın güvenlik açığının tüm boyutunu ayrıntılı olarak anlatacağım, böylece uygulamaları neyin savunmasız hale getirdiğini daha iyi anlayacaksınız.

    Güvenlik açığı bulunan uygulamanın özellikleri

    Bu kapsamlı bir liste değildir. Uygulamada özellik listesi farklılık gösterebilir!

    1. Sunucu, KeepAlive ile birlikte kullanıldığında birden fazla isteğin aynı PHP işlemi tarafından sunulmasına izin veren mod_php'yi kullanıyor

    Bu önemlidir çünkü PHP'deki rasgele sayı üreteçleri süreç başına yalnızca bir kez ekilir. Eğer sürece iki veya daha fazla istekte bulunabilirsek, süreç aynı başlangıç ​​değerini kullanacaktır. Saldırının özü, AYNI tohum değerine (yani aynı süreçte) dayalı olarak oluşturulan başka bir tokenı tahmin etmek için gerekli olan tohum değerini çıkarmak için bir tokenın genişlemesini kullanmaktır. Mod_php, ilgili rastgele değerleri almak için birden fazla sorgu kullanmak için ideal olduğundan, bazen tek bir sorguyla mt_Rand() ile ilgili birden fazla değeri almak mümkün olabilir. Bu, herhangi bir mod_php gereksinimini gereksiz hale getirir. Örneğin, mt_Rand() için tohum oluşturmak için kullanılan entropinin bir kısmı, aynı istekteki oturum kimlikleri veya çıktı değerleri üzerinden sızabilir.

    2. Sunucu, mt_Rand() belirteçlerine dayalı olarak oluşturulan CSRF belirteçlerini, parola sıfırlama belirteçlerini veya hesap onay belirteçlerini ortaya çıkarır

    Tohum değerini çıkarmak için PHP'deki üreteçlerin ürettiği sayıyı doğrudan kontrol etmemiz gerekir. Ve nasıl kullanıldığı önemli değil. İster mt_Rand() çıktısı olsun, ister hash edilmiş bir CSRF ya da bir hesap doğrulama belirteci olsun, mevcut herhangi bir değerden bunu çıkartabiliriz. Rastgele bir değerin çıktıda farklı bir davranışı belirlediği ve bu değeri ortaya çıkaran dolaylı kaynaklar bile uygundur. Ana sınırlama, tahmin etmeye çalıştığımız ikinci tokenı üreten aynı süreçten olması gerektiğidir. Bu da bir “bilgi ifşası” güvenlik açığıdır. Yakında göreceğimiz gibi, PRNG çıktısının sızdırılması son derece tehlikeli olabilir. Güvenlik açığının tek bir uygulamayla sınırlı olmadığını unutmayın: Her ikisi de aynı PHP işlemini kullandığı sürece, sunucudaki bir uygulamanın PRNG çıktısını okuyabilir ve bunu aynı sunucudaki başka bir uygulamanın çıktısını belirlemek için kullanabilirsiniz.

    3. Bilinen zayıf token oluşturma algoritması

    Bunu hesaplayabilirsiniz:

    • açık kaynak uygulamasının kaynaklarını araştırdıktan sonra,
    • bir çalışana kişisel kaynak koduna erişmesi için rüşvet vermek,
    • eski işverenine kin besleyen eski bir çalışanı bulmak,
    • veya basitçe hangi algoritmanın olabileceğini tahmin etmek.

    Bazı token oluşturma yöntemleri daha belirgindir, bazıları ise daha popülerdir. Gerçekten zayıf nesil, PHP'nin rastgele sayı üreteçlerinden birinin (örn. mt_Rand()), zayıf entropinin (başka tanımlanmamış veri kaynağı yok) ve/veya zayıf karmanın (örn. MD5 veya hiç karma yok) kullanılması anlamına gelir. Yukarıda tartışılan kod örneği, zayıf bir üretim yönteminin ayırt edici özelliklerine sahiptir. Ayrıca maskelemenin her zaman tatmin edici olmayan bir çözüm olduğunu göstermek için SHA512 karmasını da kullandım. SHA512 zayıf bir karmadır çünkü hesaplaması hızlıdır; bu, bir saldırganın herhangi bir CPU veya GPU üzerindeki giriş verilerine inanılmaz bir hızda kaba kuvvet uygulayabileceği anlamına gelir. Ayrıca Moore yasasının hala yürürlükte olduğunu da unutmayın; bu, her yeni nesil CPU/GPU ile kaba kuvvet hızının artacağı anlamına gelir. Bu nedenle, işlemci performansından veya Moore Yasasından bağımsız olarak, şifrelerin kırılması sabit bir süre alan araçlar kullanılarak karma işlemi yapılmalıdır.

    Saldırı gerçekleştirme

    Saldırımız oldukça basit. PHP sürecine bağlanmanın bir parçası olarak hızlı bir oturum düzenleyeceğiz ve iki ayrı HTTP isteği göndereceğiz (A isteği ve B isteği). Oturum, ikinci istek alınana kadar sunucu tarafından tutulacaktır. A İsteği, CSRF, parola sıfırlama belirteci (saldırgana postayla gönderilen) veya benzeri bir şey gibi kullanılabilir bazı belirteçleri elde etmeyi amaçlamaktadır. Rastgele kimlik taleplerinde kullanılan satır içi işaretleme gibi diğer özellikleri de unutmayın. Orijinal tokena, bize başlangıç ​​değerini verene kadar eziyet edeceğiz. Bunların hepsi tohum kurtarma saldırısının bir parçası: tohumun entropisi çok az olduğundan kaba kuvvet uygulanabiliyor veya önceden hesaplanmış bir gökkuşağı tablosunda aranabiliyor.


    B sorgusu daha ilginç bir sorunu çözecektir. Yerel yönetici şifresinin sıfırlanması için bir istekte bulunalım. Bu, bir belirtecin oluşturulmasını tetikleyecektir (eğer her iki istek de aynı PHP işlemine başarılı bir şekilde gönderilirse, İstek A ile çektiğimiz aynı tohuma dayalı rastgele bir sayı kullanarak). Bu belirteç, yöneticinin kendisine e-postayla gönderilen parola sıfırlama bağlantısını kullanacağı anın beklentisiyle veritabanında saklanacaktır. A isteğinden belirtecin çekirdek değerini çıkarabilirsek, B isteğinden gelen belirtecin nasıl oluşturulduğunu bilerek, parola sıfırlama belirtecini tahmin edebiliriz. Bu, yönetici mektubu okumadan önce sıfırlama bağlantısını takip edebileceğimiz anlamına gelir!


    İşte olayların sırası:

    1. A isteğini kullanarak jetonu alırız ve başlangıç ​​değerini hesaplamak için ona ters mühendislik uygularız.
    2. B isteğini kullanarak aynı başlangıç ​​değerine dayalı olarak oluşturulmuş bir jeton elde ederiz. Bu belirteç, gelecekteki parola sıfırlama işlemleri için uygulama veritabanında saklanır.
    3. Sunucu tarafından oluşturulan rastgele sayıyı elde etmek için SHA512 karma değerini kırıyoruz.
    4. Ortaya çıkan rastgele değeri kullanarak, bunun yardımıyla oluşturulan başlangıç ​​​​değerine kaba kuvvet uygularız.
    5. Seed'i, muhtemelen bir parola sıfırlama belirtecinin temelini oluşturabilecek bir dizi rastgele değeri hesaplamak için kullanırız.
    6. Bu belirteçleri yönetici parolasını sıfırlamak için kullanırız.
    7. Yönetici hesabına erişim kazanıyor, eğleniyor ve fayda sağlıyoruz. En azından eğleniyoruz.

    Haydi hacklemeye başlayalım...

    Adım adım uygulama hackleme

    1. Adım. Tokenı almak için A talebinde bulunun

    Hedef jetonun ve parola sıfırlama jetonunun mt_Rand() çıktısına bağlı olduğunu varsayıyoruz. Bu nedenle onu seçmeniz gerekiyor. Hayali senaryomuzdaki uygulamada, tüm tokenler aynı şekilde üretiliyor, böylece CSRF tokenını kolayca çıkartıp daha sonra kullanmak üzere kaydedebiliyoruz.

    2. Adım. Yönetici hesabı için oluşturulan parola sıfırlama belirtecini almak için B isteğini gerçekleştirin

    Bu talep, basit bir şifre sıfırlama formunun gönderilmesidir. Belirteç veritabanına kaydedilecek ve kullanıcıya posta yoluyla gönderilecektir. Bu jetonu doğru hesaplamamız gerekiyor. Sunucu özellikleri doğruysa, istek B, istek A ile aynı PHP sürecini kullanır. Bu nedenle, mt_rand() çağrıları her iki durumda da aynı başlangıç ​​değerini kullanacaktır. Prosedürü kolaylaştırmak adına (ara gidiş dönüşten kaçınmak) gönderimi etkinleştirmek için sıfırlama formunun CSRF belirtecini yakalamak için İstek A'yı bile kullanabilirsiniz.

    3. Adım. A isteğinden alınan tokenın SHA512 karmasını hackleyin

    SHA512, programcılar arasında hayranlık uyandırıyor: SHA-2 algoritma ailesinin tamamında en büyük sayıya sahip. Ancak kurbanımızın token oluşturma yönteminde bir sorun var; rastgele değerler yalnızca sayılarla sınırlı (yani belirsizlik derecesi veya entropi ihmal edilebilir). mt_getrandmax() çıktısını kontrol ederseniz, mt_Rand() işlevinin üretebileceği en büyük rastgele sayının 2,147 milyar olduğunu ve bir miktar değişiklik olduğunu göreceksiniz. Bu sınırlı sayıda özellik, SHA512'yi kaba kuvvete karşı savunmasız hale getirir.


    Sadece benim sözlerime inanmayın. En yeni nesillerden birinden ayrı bir video kartınız varsa, aşağıdaki yolu takip edebilirsiniz. Tek bir hash aradığımız için harika bir kaba kuvvet aracı olan hashcat-lite'ı kullanmaya karar verdim. Bu, hashcat'in en hızlı sürümlerinden biridir ve Windows dahil tüm büyük işletim sistemlerinde mevcuttur.


    Bir jeton oluşturmak için bu kodu kullanın:


    $rand = mt_rand(); echo "Rastgele Sayı: ", $rand, PHP_EOL; $belirteç = hash("sha512", $rand); echo "Belirteç: ", $belirteç, PHP_EOL;

    Bu kod, A isteğinden gelen jetonu yeniden üretir (ihtiyacımız olan rastgele sayıyı içerir ve SHA512 karmasında gizlidir) ve onu hashcat aracılığıyla çalıştırır:


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

    İşte tüm bu seçeneklerin anlamı:

    • -m1700: Karma algoritmasını belirtir; burada 1700, SHA512 anlamına gelir.
    • --pw-min=1: Hashing değerinin minimum giriş uzunluğunu tanımlar.
    • --pw-max=10: Hashing değerinin maksimum giriş uzunluğunu tanımlar (mt_Rand() için 10).
    • -1?d: yalnızca sayılardan (ör. 0-9) oluşan özel bir sözlüğe ihtiyacımız olduğunu belirtir.
    • -o ./seed.txt: sonuçların yazılacağı dosya. Ekranda hiçbir şey görüntülenmiyor, bu nedenle bu seçeneği ayarlamayı unutmayın!
    • ?d?d?d?d?d?d?d?d?d?d: kullanılacak formatı belirten maske (maksimum 10'a kadar tüm rakamlar).

    Her şey doğru çalışıyorsa ve GPU'nuz erimiyorsa Hashcat, hash edilmiş rastgele sayıyı birkaç dakika içinde hesaplayacaktır. Evet dakikalar. Entropinin nasıl çalıştığını daha önce açıklamıştım. Kendin için gör. Mt_Rand() fonksiyonunun yetenekleri o kadar az ki, tüm değerlerin SHA512 karmaları aslında çok kısa sürede hesaplanabiliyor. Dolayısıyla mt_Rand() çıktısını karıştırmanın bir anlamı yoktu.

    4. Adım. Yeni saldırıya uğramış rastgele bir sayı kullanarak başlangıç ​​değerini geri yükleyin

    Yukarıda gördüğümüz gibi, mt_Rand() tarafından oluşturulan herhangi bir değeri SHA512'den çıkarmak yalnızca birkaç dakika sürer. Rastgele bir değerle donanmış olarak başka bir kaba kuvvet aracını çalıştırabiliriz - php_mt_seed. Bu küçük yardımcı program mt_Rand() fonksiyonunun çıktısını alır ve kaba kuvvetten sonra analizin oluşturulabileceği başlangıç ​​değerini hesaplar. Güncel sürümü indirin, derleyin ve çalıştırın. Derlemede sorun yaşıyorsanız eski bir sürümü deneyin (Yeni sürümlerde sanal ortamlarda sorun yaşadım).


    ./php_mt_seed

    Bu işlem CPU üzerinde yapıldığından SHA512 cracklemeye göre biraz daha uzun sürebilir. İyi bir işlemcide yardımcı program, başlangıç ​​​​değerinin olası tüm aralığını birkaç dakika içinde bulacaktır. Sonuç, bir veya daha fazla olası değerdir (yani rastgele sayıyı üretmek için kullanılabilecek değerler). Yine zayıf entropinin sonucunu görüyoruz, ancak bu sefer PHP'nin Mersenne girdap fonksiyonu için başlangıç ​​değerleri üretmesiyle ilişkili. Daha sonra bu değerlerin nasıl oluşturulduğuna bakacağız, böylece kaba kuvvetin neden bu kadar hızlı yapılabileceğini göreceksiniz.


    Bundan önce internetteki basit hackleme araçlarını kullanıyorduk. Bunlar mt_Rand() çağrılarına yöneliktir ancak diğer senaryolara uygulanabilecek bir fikri gösterirler (örneğin, belirteçler oluştururken sıralı mt_Rand() çağrıları). Ayrıca hackleme hızının, belirli token oluşturma yaklaşımlarını dikkate alan gökkuşağı tablolarının oluşturulmasını engellemediğini unutmayın. İşte mt_Rand() güvenlik açıklarından yararlanan ve Python'da yazılmış başka bir araç.

    5. Adım. Olası yönetici hesabı parola sıfırlama belirteçlerini oluşturun

    A ve B istekleri içerisinde mt_Rand()'a yalnızca iki isteğin yapıldığını varsayalım. Şimdi önceden hesaplanan olası başlangıç ​​değerlerini kullanarak jetonları tahmin etmeye başlayalım:


    function tahmin($seed) ( /** * Başlangıç ​​değerini PRNG'ye iletin */ mt_srand($seed); /** * A isteğinden işlev çağrısını atlayın */ mt_rand(); /** * Tahmin edin ve değeri döndürün istekte bir tane oluşturuldu B token */ $token = hash("sha512", mt_Rand()); return $token; )

    Bu işlev, olası her tohum için bir sıfırlama jetonunu tahmin eder.

    Adım 6 ve 7: Yönetici hesabınızın şifresini sıfırlayın ve eğlenin!

    Artık uygulamanın güvenlik açığı nedeniyle yönetici şifresini sıfırlamanıza ve hesabınıza erişim kazanmanıza olanak sağlayacak bir belirteç içeren bir URL toplamanız gerekiyor. Bir forumda veya bir makalede filtrelenmemiş HTML yayınlayabileceğinizi keşfedebilirsiniz (bu, savunma ilkesinin yaygın bir ihlalidir). Bu, uygulamanın diğer tüm kullanıcılarına kapsamlı bir XSS saldırısı gerçekleştirmenize, bilgisayarlarına kötü amaçlı yazılım ve Tarayıcıdaki Adam izleme bulaştırmanıza olanak tanır. Cidden, neden sadece erişim kazanmayı bırakasınız ki? Görünüşte pasif ve çok da tehlikeli olmayan bu güvenlik açıklarının amacı, saldırganın yavaş yavaş asıl amacına ulaşabileceği yere nüfuz etmesine yardımcı olmaktır. Hacking, bir dizi güçlü saldırıyı serbest bırakmak için doğru kombinasyona hızla basmanız gereken bir atari dövüş oyunu oynamaya benzer.

    Saldırı sonrası analiz

    Yukarıdaki senaryo ve adımların basitliği size mt_Rand() işlevinin tehlikelerini açıkça göstermelidir. Riskler o kadar açıktır ki, artık bir saldırganın herhangi bir biçimde erişebildiği, zayıf şekilde gizlenmiş mt_Rand() çıktı değerlerinin bir "bilgi ifşası" güvenlik açığı olduğunu düşünebiliriz.


    Üstelik bu hikayenin ikinci bir tarafı daha var. Örneğin, bazı önemli görevler için masum bir şekilde mt_rand() kullanan bir kütüphaneye bağımlıysanız, hatta elde edilen değerleri vermeden, o zaman ihtiyaçlarınız için "sızdıran" bir belirteç kullanarak bu kütüphaneyi tehlikeye atmış olursunuz. Ve bu bir sorundur çünkü kitaplık veya çerçeve, başlangıç ​​değerini geri yükleme saldırısını hafifletmek için hiçbir şey yapmaz. Kullanıcıyı mt_Rand() değerlerini sızdırdığı için mi yoksa kütüphaneyi daha iyi rastgele değerler uygulamadığı için mi suçlamalıyız?


    Aslında ikisi de oldukça suçlu. Kütüphane, önemli problemler için rastgele değerlerin tek kaynağı olarak mt_Rand()'ı (veya başka herhangi bir zayıf entropi kaynağını) seçmemelidir. Kullanıcı mt_Rand() değerlerini sızdıran kod yazmamalıdır. Yani evet, doğrudan sızıntılara yol açmasa bile, mt_Rand() kullanmanın okuma yazma bilmeyen örneklerine suçlayıcı bir parmakla işaret etmeye başlayabilirsiniz.


    Endişelenmeniz gereken yalnızca bilgilerin ifşa edilmesine ilişkin güvenlik açıkları değildir. Uygulamaları, teknik olarak kriptografik olmayan ancak önemsiz uygulama işlevlerinin çalıştırılmasında kullanılan hassas belirteçlerin, anahtarların veya nonce'ların kaba kuvvetine karşı savunmasız bırakan entropi güvenlik açıklarının bulunmadığının farkında olmak da önemlidir.

    Ve şimdi her şey aynı

    Artık PHP'de yerleşik PRNG'leri kullanmanın entropi güvenlik açığının eksikliği olarak kabul edildiğini biliyoruz (yani belirsizliğin azaltılması kaba kuvveti kolaylaştırır). Saldırımızı genişletebiliriz:


    Bilginin açığa çıkması güvenlik açığı, bu belirteç oluşturma yöntemini tamamen işe yaramaz hale getiriyor. Nedenini anlamak için PHP'nin uniqid() fonksiyonuna daha yakından bakalım. Tanımı:


    Mikrosaniye cinsinden geçerli zamanı temel alarak benzersiz bir önek tanımlayıcı elde eder.


    Hatırlayacağınız gibi entropi belirsizliğin bir ölçüsüdür. Bilginin açığa çıkması güvenlik açığı nedeniyle mt_Rand() tarafından oluşturulan değerler sızdırılabilir, bu nedenle mt_Rand() işlevini benzersiz tanımlayıcı öneki olarak kullanmak sıfır belirsizlik katar. Örneğimizde uniqid() işlevinin diğer tek girdi türü zamandır. Ancak kesinlikle belirsiz DEĞİLDİR. Doğrusal ve öngörülebilir bir şekilde değişir. Ve öngörülebilir değerler son derece düşük entropiye sahiptir.


    Elbette tanım "mikrosaniye"yi, yani saniyenin milyonda birini ifade ediyor. Bu bize 1.000.000 olası sayıyı verir. Burada 1 saniyeden büyük değerleri göz ardı ediyorum çünkü kesirleri ve ölçülebilirlikleri o kadar büyük ki (örneğin, bir yanıttaki HTTP Tarih başlığı) neredeyse hiçbir şey vermiyor. Detaylara girmeden önce uniqid() fonksiyonunu inceleyelim ve C koduna bakalım:


    gettimeofday((struct timeval *) &tv, (struct timezone *) NULL); sn = (int) tv.tv_sec; usec = (int) (tv.tv_usec % 0x100000); /* usec'in maksimum değeri 0xF423F olabilir, bu nedenle * usecs'te yalnızca beş onaltılı sayı kullanırız. */ 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", önek, sn, usec); ) RETURN_STRING(uniqid, 0);

    Eğer bu çok karmaşık görünüyorsa, eski güzel PHP'deki her şeyi kopyalayabilirsiniz:


    function benzersiz_id($prefix = "", $more_entropy = false) ( list($usec, $sec) = patlama(" ", 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, $ sn, $usec); ))

    Bu kod bize, hiçbir parametre olmadan uniqid() öğesini çağırmanın bize 13 karakterlik bir dize döndüreceğini söyler. İlk 8 karakter, onaltılık sayıyla ifade edilen geçerli Unix zaman damgasıdır (saniye cinsinden). Son 5 karakter onaltılık sistemde ek mikrosaniyelerdir. Başka bir deyişle, temel uniqid() işlevi, sistem zamanının çok hassas bir ölçümünü sağlar; bu, aşağıdaki gibi bir kodla uniqid()'e yapılan basit bir çağrıdan elde edilebilir:


    $id = uniqid(); $zaman = str_split($id, 8); $sn = hexdec("0x" . $zaman); $usec = hexdec("0x" . $zaman); echo "Saniye: ", $saniye, PHP_EOL, "Mikrosaniye: ", $usec, PHP_EOL;

    C koduna bakın. Parametrelerden bağımsız olarak tam sistem zamanı hiçbir zaman çıktıda gizlenmez:


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

    Kaba kuvvet benzersiz tanımlayıcıları

    Düşünüldüğünde, herhangi bir uniqid() değerinin bir saldırgana ifşa edilmesinin, potansiyel bilgilerin açığa çıkması güvenlik açığının bir başka örneği olduğu açıkça ortaya çıkıyor. Bu, sonraki uniqid() çağrıları için girdiyi tahmin etmek için kullanılabilecek çok hassas sistem zamanını sızdırıyor. Bu, 1.000.000 olasılığı daha dar bir aralığa daraltarak mikrosaniyeleri tahmin etmeye çalışırken ortaya çıkan ikilemleri çözmeye yardımcı olur. Bu sızıntıdan daha sonra bahsedilebileceği için örneğimizde teknik olarak buna gerek yoktur. Orijinal uniqid() jeton koduna tekrar bakalım:


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

    Bu örnekten, uniqid() öğesinden bilgi ifşasıyla birlikte mt_rand() üzerinde bir sıfırlama saldırısı gerçekleştirerek, nispeten küçük bir SHA512 karma kümesi hesaplayacağımızı görebiliriz; bunların parola sıfırlamaları veya diğer önemli belirteçler olduğu ortaya çıkabilir. uniqid() kaynaklı sistem zamanı sızıntısından yararlanmadan dar bir zaman damgası aralığına ihtiyacınız varsa, o zaman genellikle bir HTTP Tarih başlığı içeren sunucu yanıtlarını analiz edelim. Buradan tam sunucu zaman damgalarını alabilirsiniz. Ve bu durumda entropi bir milyon olası mikrosaniye değerine eşit olduğundan, onu birkaç saniye içinde kaba kuvvetle çalıştırabilirsiniz!


    Artan entropi bizi kurtaracak mı?

    Elbette, fonksiyonun ikinci parametresini TRUE olarak ayarlayarak uniqid() fonksiyonuna entropi eklemek mümkündür:


    C kodunun gösterdiği gibi, yeni entropi kaynağı dahili php_combined_lcg() fonksiyonunun çıktısını kullanır. Bu işlev, uniqid() işlevinin PHP dönüşümünde kullandığım lcg_value() işlevi aracılığıyla kullanıcı alanına maruz kalıyor. Temel olarak, ayrı başlangıç ​​değerleri verilen iki doğrusal uyumlu üreteç tarafından oluşturulan iki değeri birleştirir. Jeneratörlere bu başlangıç ​​değerlerini sağlayan kod aşağıdadır. mt_Rand()'da olduğu gibi, PHP işlemi başına bir kez oluşturulurlar ve sonraki tüm çağrılarda yeniden kullanılırlar.


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

    Çok uzun süre bakarsanız ve monitöre bir şey atmak istiyorsanız, yapmamak daha iyidir. Monitörler günümüzde pahalıdır.


    Her iki başlangıç ​​değeri de Unix Epoch'tan bu yana (sunucu saatine atıfta bulunarak) mevcut zamanı saniye ve mikrosaniye cinsinden yakalamak için C gettimeofday() işlevini kullanır. Her iki çağrının da kaynak kodunda uygulandığına dikkat edilmelidir, dolayısıyla aralarındaki microsecond() sayacının değeri minimum düzeyde olacaktır, bu da ortaya çıkan belirsizliği azaltır. İkinci başlangıç ​​değeri de mevcut işlemin kimliğine karıştırılacaktır ve Linux altında çoğu durumda 32.768'i geçmeyecektir.Elbette, /proc/sys/kernel/pid_max değerini değiştirerek sınırı manuel olarak yaklaşık 4 milyona yükseltebilirsiniz. ancak bu çok istenmeyen bir durumdur.


    Bu LCG'ler tarafından kullanılan birincil entropi kaynağının mikrosaniye olduğu ortaya çıktı. Örneğin mt_Rand() başlangıç ​​değerimizi hatırlıyor musunuz? Tahmin edin nasıl hesaplanıyor?


    #ifdef PHP_WIN32 #define GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C)))) #else #define GENERATE_SEED() (((long) ) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C)))) #endif

    Bu, PHP'de kullanılan tüm başlangıç ​​değerlerinin birbirine bağlı olduğu anlamına gelir. Aynı giriş verileri bile birkaç kez karıştırılır. Belki de yukarıda tartıştığımız gibi başlangıçtaki mikrosaniye aralığını sınırlayabilirsiniz: iki sorgu kullanarak, ilki saniyeler arasında atlayarak (böylece mikrozaman 0 + bir sonraki gettimeofday() C çağrısının yürütme süresi olacaktır). Kaynak koduna erişiminiz varsa, diğer gettimeofday() çağrıları arasındaki mikrosaniye deltasını bile hesaplayabilirsiniz (PHP'nin açık kaynak yapısı buna yardımcı olur). Bahsetmeye bile gerek yok, mt_Rand() ile tohuma kaba kuvvet uygulamak, size çevrimdışı doğrulamaya izin veren son bir tohum verir.


    Ancak asıl sorun php_combined_lcg()'de yatıyor. Bu, lcg_value() işlevinin başlangıç ​​değerini PHP işlemi başına bir kez alan düşük düzeyli kullanıcı alanı uygulamasıdır. Ve eğer bu değeri biliyorsanız çıktıyı tahmin edebilirsiniz. Eğer bu cevizi kırarsan, o zaman bu kadar, oyun biter.

    Bunun için bir uygulama var...

    Pratik şeylere odaklanmak için çok zaman harcadım, o yüzden onlara tekrar dönelim. Php_combined_lcg() tarafından kullanılan iki başlangıç ​​değerini elde etmek o kadar kolay değil - doğrudan sızıntı oluşturmak mümkün olmayabilir. lcg_value() işlevi nispeten az bilinmektedir ve programcılar PHP'de yerleşik PRNG'ye ihtiyaç duyduklarında genellikle mt_rand() işlevine güvenirler. lcg_value() işlevinin değeri sızdırmasını engellemek istemiyorum, ancak bu pek popüler olmayan bir işlev. Birleştirilmiş LCG çifti aynı zamanda tohumlama işlevini de yansıtmaz (bu nedenle birinin eski kodundan devralınan sızdıran bir tohumlama mekanizmasını tanımlamak için mt_srand() çağrılarını arayamazsınız). Ancak kaba kuvvet tohumları için doğrudan veri sağlayan güvenilir bir kaynak vardır: PHP'deki oturum kimlikleri.


    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, zaman damgası, mikrosaniye ve... php_combined_lcg() çıktısını kullanarak oturum kimliği için bir karma öncesi değer üretir. Mikro-zamansal olasılıkların sayısındaki önemli azalma göz önüne alındığında (bu kodun kimliği oluşturmak için 1'e ve php_combined_lcg() için 2'ye ihtiyacı vardır, bu da aralarında minimum fark oluşmasına neden olur), artık kaba kuvvet uygulayabiliriz. Muhtemelen.


    Hatırlayacağınız gibi PHP artık session.entropy_file ve session.entropy_length gibi yeni oturum seçeneklerini destekliyor. Bu, php_combined_lcg() kullanarak LCG oluşturucuları için hızlı bir şekilde (saatler sürmez) iki başlangıç ​​değerini bir araya getirebileceğiniz kaba kuvvet oturum kimliklerini önlemek için yapılır. PHP 5.3 veya daha düşük bir sürüm kullanıyorsanız bu seçenekleri yanlış yapılandırmış olabilirsiniz. Bu, LCG için başlangıç ​​değerlerini elde etmek amacıyla oturum kimliklerini kaba kuvvetle kullanmanıza olanak tanıyan başka bir yararlı bilginin açığa çıkması güvenlik açığının olduğu anlamına gelir.


    Bu gibi durumlar için LCG değerlerini hesaplamanızı sağlayan bir Windows uygulaması bulunmaktadır.


    Bu arada, LCG durumlarını bilmek, mt_Rand() işlevinin başlangıç ​​değerini nasıl aldığını anlamanıza olanak tanır; dolayısıyla bu, mt_Rand() değer sızıntısı eksikliğini aşmanın başka bir yoludur.


    Uniqid() işlevinin dönüş değerlerine entropi eklemek açısından tüm bunlar ne anlama geliyor?


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

    Bu, entropi eksikliğinden kaynaklanan potansiyel kırılganlığın bir başka örneğidir. Sızıntılarda entropiye güvenemezsiniz (onlardan sorumlu olmasanız bile!). Oturum ID'sine ilişkin bilgilerin sızması sayesinde saldırgan, bu ID'ye ek olarak eklenen entropi değerini de tahmin edebilmektedir.


    Tekrar soruyorum suçlu kim? X uygulaması uniqid() işlevini kullanıyorsa ancak aynı sunucudaki bir kullanıcı veya başka bir uygulama dahili LCG durumunu sızdırıyorsa her iki durumda da önlem almanız gerekir. Kullanıcılar, oturum kimliklerinin yeterince yüksek entropi kullandığından emin olmalı ve üçüncü taraf programcılar, rastgele değerler üretme yöntemlerinin entropiden yoksun olduğunu anlamalı, bu nedenle daha uygun alternatiflere geçmeleri gerekir (yalnızca zayıf entropi kaynakları mevcut olsa bile!) .

    Entropi arayışı içinde

    PHP tek başına güçlü entropi üretme yeteneğine sahip değildir. Güçlü entropinin güvenilir kaynakları olan işletim sistemi düzeyindeki PRNG oluşturuculardan veri aktarımı için temel bir API bile yoktur. Bu nedenle isteğe bağlı openssl ve mcrypt uzantılarına güvenmeniz gerekir. Sızdıran, öngörülebilir, düşük entropili kuzenlerinden çok daha iyi özellikler sunuyorlar.


    Ne yazık ki, her iki uzantı da isteğe bağlı olduğundan, bazı durumlarda son çare olarak zayıf entropi kaynaklarına güvenmekten başka seçeneğimiz yoktur. Bu gerçekleştiğinde, mt_Rand()'ın zayıf entropisinin ek belirsizlik kaynaklarıyla desteklenmesi, verilerinin sahte rastgele baytların alınabileceği tek bir havuzda karıştırılması gerekir. Güçlü bir entropi karıştırıcı kullanan benzer bir rastgele oluşturucu, Anthony Ferrara tarafından RandomLib kütüphanesinde zaten uygulanmıştı. Programcıların mümkün olduğunda yapması gereken şey budur.


    Karmaşık matematiksel dönüşümleri karma hale getirerek entropinizin zayıflığını gizleme isteğinden kaçının. Saldırgan birincil başlangıç ​​değerini öğrendiğinde tüm bunları tekrarlayacaktır. Bu tür hileler kaba kuvvet sırasındaki hesaplamaların miktarını yalnızca biraz artıracaktır. Unutmayın: entropi ne kadar düşük olursa belirsizlik de o kadar az olur; Belirsizlik ne kadar az olursa, fırsatların kaba kuvvete başvurması da o kadar az olur. Tek haklı çözüm, kullandığınız entropi havuzunu mevcut herhangi bir yolla arttırmaktır.


    RandomLib kütüphanesi, farklı entropi kaynaklarından gelen verileri karıştırarak ve bir saldırganın tahmin yapmak için ihtiyaç duyabileceği bilgileri yerelleştirerek rastgele baytlar üretir. Örneğin, mt_Rand(), uniqid() ve lcg_value() çıktılarını karıştırabilir, PID, bellek tüketimi, başka bir mikrozaman ölçümü, $_ENV serileştirme, posix_times() vb. ekleyebilirsiniz. Veya daha da ileri giderek, bu RandomLib'e olanak tanır genişletilebilirlik. Diyelim ki bazı deltaları mikrosaniye cinsinden kullanıyoruz (yani, bir fonksiyonun hash() çağrıları gibi sahte rastgele giriş verileriyle çalışması için kaç mikrosaniyeye ihtiyacı olduğunu ölçüyoruz).

    Etiket ekle

    Rastgele sayılar, özellikle güvenlik sistemleri söz konusu olduğunda programlamanın ayrılmaz bir parçasıdır. Örneğin kriptografi, tahmin edilemeyen sayılar üretmek için rastgele değerler üretmeye dayanır. Elbette PHP'de rastgele sayılar büyük bir rol oynar: onların yardımıyla jetonlar, tuzlar ve diğer değerleri üretebiliriz.

    Rastgele sayı üretimi özel algoritmalara dayanmaktadır. Algoritmanın başlayacağı giriş parametresi rastgele bir değer veya önceden belirlenmiş bir değer olabilir.

    Bu yazıda rastgele sayılar hakkında konuşacağız: bunların nasıl oluşturulduğu ve nerede kullanılabileceği.

    Rastgele Sayıları Kullanma

    PHP'de rastgele sayılar büyük bir rol oynar çünkü... çok sık çeşitli amaçlar için kullanılır. Temel olarak güvenlikle ilgilidirler. Bunlara dayanarak CSRF belirteçleri, API anahtarları, kimlik doğrulama değerleri, parola sıfırlama değerleri ve çok daha fazlası oluşturulur. Bütün bunlar, ortaya çıkan değerin tahmin edilmesinin imkansız olması için yapılır.

    Rastgele değerleri kullanmanın en önemli örnekleri şunlardır:

    • Kriptografi için tuz üretme- kural olarak, tek yönlü şifrelemenin yanı sıra şifrelerin karma işlemi için rastgele bir tuz numarası kullanılır. Bu rastgele değer kriptografide başlatma vektörü olarak kullanılır.
    • Oturum kimliği gibi rastgele değerler oluşturun- PHP, güvenliğin ön planda olduğu çok sayıda uygulama oluşturmak için kullanılır. Çoğu işlevsellik, oturumlarla ve oluşturulan oturum kimlikleriyle çalışmaya dayanır.
    • Tahmin edilmesi neredeyse imkansız olan kimlik doğrulama belirteçlerinin oluşturulması- birçok PHP uygulaması, özel API arayüzleri aracılığıyla diğer sistemlerle çalışmaya dayanmaktadır. Genellikle bir API kullanmadan önce bir kimlik doğrulama sürecinden geçmeniz gerekir. Tokenlar için bulunması zor değerlerin elde edilmesi oldukça zordur. Bu problemlerde rastgele sayıların kullanılmasının nedeni budur.

    Rastgele sayı üreteçleri

    Yukarıda açıklanan durumlarda kullanılan rastgele sayılar, PHP'deki sözde oluşturucular tarafından üretilir. Birkaç algoritma mevcuttur:

      Lcg_value() işlevini kullanırken doğrusal uyumlu yöntem.

      Mt_Rand() işlevi tarafından kullanılan Mersenne girdabı.

      Rand() işlevi C dilinde benzer bir işlevi kullanır.

    Aslında bu işlevler rastgele sayılar döndürmez, sayıların rastgele görünecek şekilde dağıtılmasını sağlar. Bu sayıların sırası, uygulanan algoritma içindeki temel rastgele sayıya bağlıdır.

    Jeneratörler için temel sayılar

    Temel sayılar veya taban sayıların vektörleri, rastgele sayılar oluşturmak için kullanılan veri kümeleridir. Sözde rastgele sayı üreteçleri yalnızca onlardan başlayarak çalışır. Bir saldırgan bu temel sayıyı biliyorsa, gelecekte rastgele sayılarınızın değerlerini tahmin edebilecektir.

    PHP'de taban sayılarını iki şekilde belirtebilirsiniz. Birincisi mt_srand() fonksiyonunu kullanmaktır. Bu yöntem esas olarak rastgele bir serinin birim testlerinde kullanılır. İkinci yol ise PHP'nin temel sayıları kendisinin oluşturmasına izin vermektir. Sürüm 4.2'den beri PHP bu özelliği sunmaktadır. Daha sonra Mersenne Vortex rastgele sayılar üretmek için kullanılacak.

    PHP işletim sistemine bağlı olarak bir temel sayı üretir. Linux platformlarında /dev/urandom'da mcrypt_create_iv() veya openssl_pseudo_random_bytes() işlevlerini kullanabilirsiniz. Windows, openssl_pseudo_random_bytes() ve mcrypt_create_iv() işlevleri aracılığıyla erişilebilen özel bir sözde oluşturucu sağlar.

    Sonuç olarak

    Rastgele sayılar güvenli web uygulamaları oluşturmada büyük rol oynadığından bunların nasıl çalıştığı hakkında daha fazla bilgiye ihtiyacımız var. Sahte oluşturucular için temel sayıları kendiniz oluşturmak istiyorsanız yöntemlerinizin güvenilir olduğundan emin olun.

    • İtibaren:
    • Kayıtlı: 2014.07.07
    • Gönderiler: 3,775
    • Sadece PunBB'yi seviyorum:
    • 5 yıllar, 6 aylar,
    • Seviyor: 463

    Konu: PHP'de bir diziden rastgele bir değer nasıl seçilir

    Bu fonksiyonu kullanarak dizinin rastgele bir elemanını (veya elemanlarını) seçebiliriz. Evet, tam olarak element veya elementler! Bu bir unsur olabileceği gibi birden fazla da olabilir. Her şey karşı karşıya olduğunuz göreve bağlıdır.

    Ancak burada fonksiyonun öğenin değerini değil, anahtarını (veya birkaç öğe varsa anahtarlarını) döndüreceği dikkate alınmalıdır.

    Fonksiyon parantez içinde parametre olarak alır: üzerinde çalıştığımız dizinin adı ve seçilmesi gereken eleman sayısı.

    Genel olarak her şey basit! Ve tüm bunlara örneklerle baktığımızda her şey daha da kolaylaşacaktır.

    Önce diziden tek bir rastgele öğe seçelim.

    2 Yanıtlayan: PunBB

    • İtibaren: Moskova, Sovkhoznay 3, daire. 98
    • Kayıtlı: 2014.07.07
    • Gönderiler: 3,775
    • Sadece PunBB'yi seviyorum:
    • 5 yıllar, 6 aylar,
    • Seviyor: 463

    Web sitemizin üst kısmında bir yerde bazı alıntıları görüntülemek istediğimizi hayal edelim. Tabii ki tekliflerin değişmesi gerekiyor. Bir kullanıcı sitenizi her ziyaret ettiğinde kullanıcının yeni bir fiyat teklifi görmesini istersiniz.

    Muhtemelen tahmin ettiğiniz gibi, bunu uygulamanın en kolay yolu, mevcut tüm alıntıları ve deyişleri bir diziye yerleştirmek ve ardından bu diziden rastgele bir öğe seçip ekranda görüntülemektir.

    Dizide ne kadar çok alıntı varsa, bunların tekrarlanma olasılığı da o kadar az olur.

    Ama örnek olması açısından fazla uğraşmayacağım ve 7 tane sözü dizime koyacağım.

    Daha sonra array_Rand() fonksiyonunun sonucunu saklayacağım bir değişken yaratmam gerekecek. Parantez içinde bu işlevin iki argümanı olacaktır: dizimizin adı ve ihtiyacımız olan rastgele öğelerin sayısı.

    Daha önce de söylediğim gibi, işlev öğenin değerini değil, anahtarını (veya listedeki numarasını) döndürür. Böylece rastgele elemanın anahtarı değişkende saklanacaktır.

    Daha sonra sadece istenen elemanın değerini göstermem gerekiyor. Bunu yapmak için dizinin adını ve köşeli parantez içinde rastgele bir anahtar içeren değişkenimizin adını belirtiyorum.

    Bu kadar. Aşağıdaki koda bakın, sanırım her şeyi tamamen anlayacaksınız:

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

    3 Yanıtlayan: PunBB

    • İtibaren: Moskova, Sovkhoznay 3, daire. 98
    • Kayıtlı: 2014.07.07
    • Gönderiler: 3,775
    • Sadece PunBB'yi seviyorum:
    • 5 yıllar, 6 aylar,
    • Seviyor: 463

    Re: PHP'de bir diziden rastgele bir değer nasıl seçilir?

    Şimdi birkaç rastgele dizi öğesini yazdırmayı deneyelim.

    Bir öğe olması durumunda, anahtarı döndürülür ve birden fazla rastgele dizi öğesi olması durumunda, bir anahtar dizisi döndürülür. Ekranda görüntülerken başlayacağımız şey budur.

    Öncelikle içine 7 farklı isim ekleyeceğimiz bir dizi oluşturalım.

    Daha sonra array_Rand() fonksiyonunun çalışmasının kaydedileceği bir değişken yaratıyoruz. Ancak şimdi bu fonksiyon için parantez içinde ikinci argüman olarak “2” sayısını belirtiyoruz. Bu, 2 rastgele öğeye ihtiyacımız olduğu anlamına gelecektir.

    Bu durumda fonksiyonun sonucu, ana dizimizdeki öğelerin rastgele iki anahtarını içeren bir dizi olacaktır.

    Bu nedenle, ekranda görüntülerken bunu dikkate almanız ve köşeli parantez içinde yalnızca değişkenin adını değil, değişkenin adını, ardından köşeli parantezleri ve dizi indeksini belirtmeniz gerekir. 2 elementimiz olduğundan, ilk durumda indeks, ikincisinde ise olacaktır. (Dizilerdeki indekslemenin "0"dan başladığını hatırlarsınız.)

    Bu kadar. İşleri daha net hale getirmek için koda bir göz atın:

    $isimler = array("Maşa", "Saşa", "Nadya", "Mila", "Andrey", "Sergey", "Anton"); $rand_names = array_rand($isimler,2); Eko "

    ".$isimler[$rand_isimler]." ve ".$names[$rand_names]."

    ";

    Sonuç olarak ekranda iki rastgele isim görüntülenecektir. Sayfa her yenilendiğinde isimler değişecektir.

    Kaynak