Java.util.Random ve java.security.SecureRandom arasındaki fark


203

Ekibim, rastgele jetonlar üreten bazı sunucu tarafı kodlarını (Java'da) teslim etti ve ben de bununla ilgili bir sorum var -

Bu belirteçlerin amacı oldukça hassastır - oturum kimliği, parola sıfırlama bağlantıları vb. Jeton "uzun" dır, bu yüzden 64 bit uzunluğundadır.

Kod şu anda java.util.Randombu simgeleri oluşturmak için sınıfı kullanıyor . Dokümantasyon için java.util.Randomaçıkça şöyle der:

Java.util.Random örnekleri kriptografik olarak güvenli değildir. Bunun yerine güvenliğe duyarlı uygulamalar tarafından kullanılmak üzere şifreli olarak güvenli bir sahte rasgele sayı üreteci almak için SecureRandom kullanmayı düşünün.

Ancak, kod şu anda kullanma şekli java.util.Randombudur - java.security.SecureRandomSınıfı başlatır ve daha sonra sınıfı örneklemek için SecureRandom.nextLong()kullanılan tohumu elde etmek için yöntemi kullanır java.util.Random. Sonra java.util.Random.nextLong()belirteci oluşturmak için yöntem kullanır .

Öyleyse şimdi sorum - Kullanılıyorsa java.util.Randomtohumlanması hala güvensiz java.security.SecureRandommi? Kodu java.security.SecureRandomyalnızca jetonları oluşturmak için kullanacak şekilde değiştirmem gerekir mi?

Şu anda kod tohum Randombaşlangıçta bir kez


14
Tohumlandıktan sonra, java.util.Random çıktısı deterministik sayı dizisidir. Bunu istemeyebilirsiniz.
Peter Štibraný

1
Kod Randombaşlangıçta bir kez mi ekiyor, yoksa her jeton için yeni bir tane mi ekliyor? Umarım, bu aptalca bir soru, ama kontrol edeceğimi düşündüm.
Tom Anderson

8
Rasgele yalnızca 48 bit dahili duruma sahiptir ve nextLong () öğesine 2 ^ 48 çağrıdan sonra tekrarlanır; bu, tüm olası değerleri longveya doubledeğerleri üretmeyeceği anlamına gelir .
Peter Lawrey

3
Başka bir ciddi sorun daha var. 64 bit, sofistike bir saldırıya dayanamayacak kadar az olan 1.84 * 10 ^ 19 olası kombinasyon anlamına gelir. Orada 56 bit DES kodunu (faktör 256 daha az) 60 saatte saniyede 90 * 10 ^ 9 tuşla parçalayan makineler var. 128 bit veya iki uzun kullanın!
Thorsten S.

Yanıtlar:


233

Standart Oracle JDK 7 uygulaması, rasgele değerler üretmek için Doğrusal Konjuge Jeneratör olarak adlandırılanı kullanır java.util.Random.

Alınan java.util.Randomyönteme bir yorumu, kaynak kodu (JDK 7u2) protected int next(int bits)rastgele değerler üretir biridir:

Bu, DH Lehmer tarafından tanımlandığı ve Donald E. Knuth tarafından Bilgisayar Programlama Sanatı, Cilt 3: Seminumerical Algorithms , bölüm 3.2.1 tarafından tarif edildiği gibi doğrusal bir eşzamanlı sayı üretecidir .

Doğrusal Eşlenik Jeneratörlerin Öngörülebilirliği

Hugo Krawczyk, bu LCG'lerin nasıl tahmin edilebileceğine dair oldukça iyi bir makale yazdı ("Uyumlu jeneratörler nasıl tahmin edilir"). Şanslı ve ilgiliyseniz, yine de web'de ücretsiz, indirilebilir bir sürümünü bulabilirsiniz. Güvenlik açısından kritik amaçlar için asla bir LCG kullanmamanız gerektiğini açıkça gösteren çok daha fazla araştırma var . Bu aynı zamanda rastgele sayılarınızın şu anda tahmin edilebilir olduğu, oturum kimlikleri ve benzeri için istemediğiniz bir şey olduğu anlamına gelir .

Doğrusal Konjuge Jeneratör nasıl kırılır

Bir saldırganın, tam bir döngüden sonra LCG'nin tekrarlanmasını beklemesi gerektiği varsayımı yanlıştır. Optimal bir döngüde bile (tekrarlama ilişkisindeki modül m), gelecekteki değerleri tam bir döngüden çok daha kısa sürede tahmin etmek çok kolaydır. Sonuçta, sadece çözülmesi gereken bir grup modüler denklem, LCG'nin yeterli çıkış değerlerini gözlemlediğinizde kolaylaşır.

Güvenlik "daha iyi" bir tohumla gelişmez. Sadece SecureRandombir kalıbın birkaç kez yuvarlanmasıyla üretilen rastgele bir değerle tohumlamanız, hatta değeri üretmeniz önemli değildir .

Saldırgan, sadece gözlenen çıktı değerlerinden tohumu hesaplar. Bu durumda 2 ^ 48'den önemli ölçüde daha az zaman alır java.util.Random. Kâfirler bu deneyi deneyebilir , burada gelecekteki Randomçıktıları kabaca 2 ^ 16 zaman içinde sadece iki (!) Çıktı değerini gözlemleyerek tahmin edebilirsiniz . Şu anda rastgele sayılarınızın çıktısını tahmin etmek modern bir bilgisayarda bile bir saniye bile almıyor.

Sonuç

Mevcut kodunuzu değiştirin. Sadece kullanın SecureRandom. En azından sonucun tahmin edilmesinin zor olacağına dair küçük bir garantiniz olacak. Kriptografik olarak güvenli bir PRNG'nin özelliklerini istiyorsanız (sizin durumunuzda, istediğiniz budur), o zaman SecureRandomsadece gitmeniz gerekir . Kullanılma şeklini değiştirmek konusunda akıllı olmak neredeyse her zaman daha az güvenli bir şeyle sonuçlanacaktır ...


4
Çok yararlı olabilir, ayrıca SecureRandom'un nasıl çalıştığını da açıklayabilirsiniz (Rastgele nasıl çalıştığını açıkladığınız gibi) ..
gresdiplitude

4
GüvenliRandom
Azulflame

Biliyorum, bu dersi zor yoldan öğrendim. Ancak sert bir şifre ve bulunması zor bir kaynak iyi çalışır. Notch bu konuda bir şeyler öğrenebilir (kullanıcı şifresini bir .lastlogin dosyasında kodlar, anahtar olarak "passwordfile" kullanarak temel şifreleme ile kodlanır)
Azulflame

1
Burada asıl soru: java benzer bir API ile daha güvenli bir prng üretebiliyorsa, neden kırık olanı değiştirmediler?
Joel Coehoorn

11
@JoelCoehoorn Bu Randombozuk değil - sadece farklı senaryolarda kullanılmalıdır. Tabii ki, her zaman SecureRandom'u kullanabilirsiniz. Ancak genel olarak, SecureRandomsaftan belirgin şekilde daha yavaştır Random. Ve sadece iyi istatistiksel özellikler ve mükemmel performansla ilgilendiğiniz durumlar var, ancak güvenliği gerçekten önemsemiyorsunuz: Monte-Carlo simülasyonları iyi bir örnektir. Benzer bir cevapta bu konuda yorum yaptım , belki de faydalı bulabilirsin.
kabartma

72

Bir rasgele yalnızca 48 bite sahiptir, burada SecureRandom 128 bite kadar çıkabilir. Bu nedenle, güvenli sekilde tekrarlama şansı çok azdır.

Rastgele kullandığı system clocktohum olarak / veya tohum üretmek için. Böylece, saldırgan tohumun üretildiği zamanı biliyorsa kolayca çoğaltılabilir. Ama SecureRandom alır Random Datasenin dan os(- - En os bu veriler dosyalarda saklayabilirsiniz toplamak onlar tuş vuruşları vb arasındaki aralık olabilir /dev/random and /dev/urandom in case of linux/solariso tohumdan olarak ve kullanımları).
Bu nedenle, küçük simge boyutu uygunsa (Rastgele durumunda), tohumu oluşturmak için SecureRandom kullandığınızdan, kodunuzu herhangi bir değişiklik yapmadan kullanmaya devam edebilirsiniz. Ancak daha büyük jetonlar (tabi brute force attackstutulamayacak) istiyorsanız SecureRandom ile gidin -
Rastgele sadece 2^48denemeler gerektiğinde, bugünün gelişmiş cpu'su ile pratik zamanda kırmak mümkündür. Ancak 2^128, günümüzün gelişmiş makinelerinde bile kırılması yıllar ve yıllar sürecek güvenli güvenlik girişimleri gerekecektir.

Daha fazla ayrıntı için bu bağlantıya bakın.
DÜZENLEME
@emboss tarafından sağlanan bağlantıları okuduktan sonra, tohumun, ne kadar rastgele olsa da, java.util.Random ile kullanılmaması gerektiği açıktır. Çıktıyı gözlemleyerek tohumun hesaplanması çok kolaydır.

SecureRandom için git - Yerel PRNG'yi kullanın (yukarıdaki bağlantıda verildiği gibi) /dev/random, her bir çağrı için dosyadan rastgele değerler aldığındannextBytes(). O içeriğini kontrol sürece çıktı gözlemleyerek bir saldırganın mümkün olmayacaktır Bu şekilde hiçbir şey yapmak /dev/randomdosyası (ki çok düşüktür) sha1 prng algoritma hesaplar sadece bir kez ve VM eğer tohum aynı kullanarak aylarca çalışıyor çıkışı pasif olarak gözlemleyen bir saldırgan tarafından kırılabilir. NOT - İşletim sisteminizin rasgele bayt (entropi) yazabileceğinden daha hızlı çağırıyorsanız, NATIVE PRNG'yi kullanırken sorun yaşayabilirsiniz . Bu durumda, SecureRandom'un bir SHA1 PRNG örneğini kullanın ve her birkaç dakikada bir (veya bazı aralıklarla), bu örneği


nextBytes()/dev/randomnextBytes()bir NATRAL PRNG örneğidir. Bu ikisini paralel olarak çalıştırmak, İşletim Sistemi tarafından elde edilen entropiyi tüketmemekle birlikte, gerçek rastgele değerlerle düzenli olarak tohumlama yapmanızı sağlayacaktır.


A'nın tahmin edilmesi 2 ^ 48'den daha az gerektirir Random, OP hiç kullanılmamalıdır Random.
kabartma

@emboss: Bruteforce hakkında konuşuyorum.
Ashwin

1
Linux ile ilgilenin: entropi tükenmesine ulaşabilir (VM'de donanımdan daha fazla)! Bakın /proc/sys/kernel/random/entropy_availve okurken bazı uzun dökümler olmadığını kontrol edin/dev/random
Yves Martin

2
Oracle JRE'nin (en az 1.7) varsayılan olarak / dev / urandom ve / dev / random ile çalışmadığına dikkat edin, böylece cevabınızın soneki artık doğru değildir. Secureerandom.source özelliği için $ JAVA_HOME / lib / security / java.security kontrol edin
Boaz

1
Java.security dosyamızın fileerandom.source = file: file / // dev / urandom yerine dosya: /// dev / urandom (dosya protokolü için iki nokta üst üste işaretinden sonra iki eğik çizgi, daha sonra dosya sisteminin kökü için bir eğik çizgi daha) / dev / random olarak entropi havuzunun tükenmesiyle ilgili sorunlara neden oldu. Düzenlenemedi, bu yüzden uygulama başlangıcında doğru olana java.security.egd bir sistem özelliği ayarlamak zorunda kaldı.
maxpolk

11

java.util.Random.nextLong()Aynı tohumla iki kez koşarsanız, aynı numarayı üretecektir. Güvenlik nedeniyle bağlı kalmak istediğiniz için java.security.SecureRandomçok daha az tahmin edilebilir.

2 Sınıflar Ben sadece değişime gerek, benzer Randomüzere SecureRandombir üstlenmeden aracıyla ve mevcut kodun en çalışacaktır.


11
Herhangi bir PRNG'nin iki örneğini alıp aynı değerle tohumlarsanız, SecureRandom kullanıldığında bile her zaman aynı rasgele sayıları alırsınız. Tüm PRNG'ler belirleyicidir ve bu nedenle tohumu biliyorsanız tahmin edilebilir.
Robert

1
Farklı SecureRandom uygulamaları vardır, bazıları PRNG'ler, bazıları değildir. Öte yandan, java.util.Random daima PRNG'dir (Javadoc'ta tanımlandığı gibi).
Peter Štibraný

3

Mevcut kodunuzu değiştirmek uygun maliyetli bir görevse, Javadoc'ta önerilen SecureRandom sınıfını kullanmanızı öneririm.

Random sınıf uygulamasını bulsanız bile, SecureRandom sınıfını dahili olarak kullanır. bunu kabul etmemeniz gerekir:

  1. Diğer VM uygulamaları da aynı şeyi yapar.
  2. Random sınıfının JDK'nın gelecekteki sürümlerinde uygulanması hala SecureRandom sınıfını kullanıyor

Bu nedenle, dokümantasyon önerisini takip etmek ve doğrudan SecureRandom ile gitmek daha iyi bir seçimdir.


Orijinal sorunun java.util.Randomuygulamanın SecureRandomdahili olarak kullanıldığını belirttiğine inanmıyorum, kodlarınıSecureRandom tohumlamak için kullandığını söyledi Random. Yine de her iki yanıta da katılıyorum; SecureRandomaçıkça belirleyici bir çözümden kaçınmak için kullanmak en iyisidir .
Palpatim

2

Mevcut referans uygulama java.util.Random.nextLong()yöntemine iki çağrı yapan , doğrudan akım tohum 32 bit ortaya koymaktadır:next(int)

protected int next(int bits) {
    long nextseed;
    // calculate next seed: ...
    // and store it in the private "seed" field.
    return (int)(nextseed >>> (48 - bits));
}

public long nextLong() {
    // it's okay that the bottom word remains signed.
    return ((long)(next(32)) << 32) + next(32);
}

Sonucun üst 32 biti nextLong(), o zamandaki tohum bitleridir. Tohumun genişliği 48 bit olduğu için (javadoc diyor), ikinci 32 biti üreten tohumu belirlemek için kalan 16 bitin (sadece 65.536 deneme) tekrarlanması * yeterlidir.

Tohum bilindikten sonra, aşağıdaki tüm jetonlar kolayca hesaplanabilir.

Çıktısını kullanarak nextLong()doğrudan, tüm gizli çok az efford ile hesaplanabilir bir dereceye kadar PNG kısmen gizli. Tehlikeli!

* İkinci 32 bit negatifse biraz çaba sarf etmek gerekir, ancak bunu öğrenebiliriz.


Doğru. Java.util.random'u jazzy.id.au/default/2010/09/20/… adresinde nasıl hızlı bir şekilde kıracağınızı görün !
ingyhere

2

Tohum anlamsız. İyi bir rasgele jeneratör seçilen primer sayıdan farklıdır. Her rastgele jeneratör bir sayıdan başlar ve bir 'halka' aracılığıyla yinelenir. Bu, eski dahili değeri olan bir sayıdan diğerine gelirsiniz. Fakat bir süre sonra tekrar başlangıca ulaşır ve her şeye yeniden başlarsınız. Yani döngüleri yürütüyorsunuz. (rastgele bir üretecin dönüş değeri dahili değer değildir)

Bir zil sesi oluşturmak için asal bir sayı kullanırsanız, mümkün olan tüm numaralar arasında tam bir döngü tamamlamadan önce o zil sesindeki tüm numaralar seçilir. Asal olmayan sayılar alırsanız, tüm sayılar seçilmez ve daha kısa döngüler alırsınız.

Daha yüksek asal sayılar, ilk öğeye tekrar dönmeden önce daha uzun döngüler anlamına gelir. Böylece, güvenli rastgele üretecin sadece daha uzun bir döngüye sahip olması, tekrar başlangıca ulaşmadan önce, bu yüzden daha güvenlidir. Sayı üretimini daha kısa döngülerde olduğu kadar kolay tahmin edemezsiniz.

Başka bir deyişle: Hepsini değiştirmek zorundasınız.


0

Rastgele ve secureRandom arasındaki farkı ve SecureRandom Sınıfının önemini kolayca anlayabilmeniz için çok temel kelimeler kullanmaya çalışacağım.

OTP'nin (bir defalık şifre) nasıl oluşturulduğunu hiç merak ettiniz mi? Bir OTP oluşturmak için Random ve SecureRandom sınıflarını da kullanırız. Şimdi OTP'nizi güçlü kılmak için SecureRandom daha iyidir çünkü 2 ^ 128 denemesi aldı, mevcut makine tarafından neredeyse imkansız olan OTP'yi kırmak, ancak Rastgele Sınıf kullanılırsa OTP'niz, verilerinize zarar verebilecek biri tarafından kırılabilir. sadece 2 ^ 48 denemek, çatlamak.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.