Różnica między Javą.util.Random i java.Ochrona.SecureRandom

Mój zespół dostał jakiś kod po stronie serwera (w Javie), który generuje losowe tokeny i mam pytanie dotyczące tego samego-

Cel tych tokenów jest dość wrażliwy - używany do identyfikatorów sesji, linków resetowania hasła itp. Więc muszą być kryptograficznie losowe, aby uniknąć ich odgadnięcia przez kogoś lub brutalnie je zmusić. Token jest "długi", więc ma długość 64 bitów.

Kod obecnie używa klasy java.util.Random do generowania tych tokenów. Dokumentacja ([ http://docs.oracle.com/javase/7/docs/api/java/util/Random.html][1]) for java.util.Random jasno stwierdza, co następuje:

Instancje Javy.util.Losowe nie są zabezpieczone kryptograficznie. Zamiast tego rozważ użycie SecureRandom, aby uzyskać kryptograficznie Bezpieczny generator liczb pseudolosowych do użytku przez aplikacje wrażliwe na bezpieczeństwo.

Jednak sposób, w jaki kod jest obecnie używany java.util.Random jest taki - tworzy instancję klasy java.security.SecureRandom, a następnie używa metody SecureRandom.nextLong() aby uzyskać ziarno, które jest używane do tworzenia instancji klasy java.util.Random. Następnie używa metody java.util.Random.nextLong() do wygenerowania tokena.

Więc moje pytanie teraz - czy to nadal niepewne biorąc pod uwagę, że {[0] } jest seeded za pomocą java.security.SecureRandom? Czy muszę modyfikować kod tak, aby używał java.security.SecureRandom wyłącznie do generowania tokenów?

Obecnie zalążek kodu jest Random raz przy starcie

Author: Robert Harvey, 2012-06-15

7 answers

Standardowa implementacja Oracle JDK 7 używa tak zwanego liniowego generatora kongruencyjnego do generowania losowych wartości w java.util.Random.

Zaczerpnięte z java.util.Random kodu źródłowego (JDK 7u2), z komentarza do metody protected int next(int bits), która generuje losowe wartości:

Jest to liniowy kongruencyjny generator liczb pseudorandomowych, jak zdefiniowany przez D. H. Lehmera i opisany przez Donalda E. Knutha w Sztuka programowania komputerowego. tom 3: algorytmy Seminumeryczne, sekcja 3.2.1.

Przewidywalność liniowych generatorów Kongruencyjnych

Hugo Krawczyk napisał całkiem dobrą pracę o tym, jak te LCG można przewidzieć ("jak przewidzieć Generatory kongruencyjne"). Jeśli masz szczęście i jesteś zainteresowany, nadal możesz znaleźć darmową wersję do pobrania w Internecie. I jest mnóstwo innych badań, które wyraźnie pokazują, że nigdy nie powinieneś używać LCG do celów krytycznych dla bezpieczeństwa. Oznacza to również że Twoje losowe liczby przewidywalne w tej chwili, coś, czego nie chcesz dla identyfikatorów sesji i tym podobnych.

Jak złamać liniowy Generator Kongruencyjny

Założenie, że atakujący musiałby czekać na powtórzenie LCG po pełnym cyklu, jest błędne. Nawet przy optymalnym cyklu (moduł m w relacji nawrotowej) bardzo łatwo jest przewidzieć przyszłe wartości w znacznie krótszym czasie niż pełny cykl. W końcu to tylko kilka równań modularnych, które potrzebują do rozwiązania, co staje się łatwe, gdy tylko zauważysz wystarczającą ilość wartości wyjściowych LCG.

Bezpieczeństwo nie poprawia się dzięki "lepszemu" nasionowi. Po prostu nie ma znaczenia, czy seed z losową wartością generowaną przez SecureRandom, czy nawet produkować wartość przez walcowanie matrycy kilka razy.

Atakujący po prostu obliczy ziarno na podstawie obserwowanych wartości wyjściowych. To zajmuje znacznie mniej czasu niż 2^48 W przypadku java.util.Random. Niewierni mogą wypróbować to eksperyment , w którym pokazano, że można przewidzieć przyszłość Random wyjścia obserwując tylko dwa(!) wartości wyjściowe w czasie około 2^16. Na nowoczesnym komputerze przewidzenie wyników losowych liczb w tej chwili nie zajmuje nawet sekundy.

Podsumowanie

Zastąp bieżący kod. Używaj wyłącznie SecureRandom. Wtedy przynajmniej będziesz miał małą gwarancję, że wynik będzie trudny do przewidzenia. Jeśli chcesz mieć właściwości Kryptograficznie zabezpieczonego PRNG (w Twoim przypadku, to jest to, czego chcesz), Następnie musisz iść z SecureRandom tylko. Bycie sprytnym w kwestii zmiany sposobu, w jaki miało być używane, prawie zawsze skutkuje czymś mniej bezpiecznym...

 217
Author: emboss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-02 00:38:14

Random ma tylko 48 bitów, gdzie as SecureRandom może mieć do 128 bitów. Szanse na powtórzenie w securerandom są więc bardzo małe.

Random używa system clock jako nasion / lub do generowania nasion. Można je więc łatwo odtworzyć, jeśli atakujący zna czas, w którym ziarno zostało wygenerowane. Ale SecureRandom bierze Random Data z twojego os (mogą to być interwały między naciśnięciami klawiszy itp-Większość os zbiera te dane przechowuje je w plikach - /dev/random and /dev/urandom in case of linux/solaris) i używa tego jako nasiona.
więc jeśli mały rozmiar tokena jest w porządku (w przypadku losowym), możesz nadal używać kodu bez żadnych zmian, ponieważ używasz SecureRandom do generowania nasion. Ale jeśli chcesz większe tokeny (które nie mogą być przedmiotem brute force attacks) Idź z SecureRandom-
w przypadku losowych tylko 2^48 próby są wymagane, z dzisiejszych zaawansowanych procesorów jest to możliwe, aby złamać go w praktycznym czasie. Ale dla securerandom 2^128 będą wymagane próby, które będą trwać latami i latami, aby zerwać nawet z dzisiejsze zaawansowane maszyny.

Zobacz ten link, aby uzyskać więcej szczegółów.
EDIT
Po przeczytaniu linków podanych przez @ emboss, jest jasne, że ziarno, jakkolwiek przypadkowe, może, nie należy stosować z java.util.Przypadkowe. Bardzo łatwo jest obliczyć ziarno, obserwując wydajność.

Wybierz SecureRandom - użyj natywnego PRNG (Jak podano w linku powyżej), ponieważ pobiera losowe wartości z pliku /dev/random dla każdego wywołania nextBytes(). To sposób, w jaki atakujący Obserwujący wyjście nie będzie w stanie wykryć niczego, chyba że kontroluje zawartość pliku /dev/random (co jest bardzo mało prawdopodobne)
Algorytm sha1 prng oblicza ziarno tylko raz i jeśli twoja maszyna wirtualna działa przez wiele miesięcy przy użyciu tego samego ziarna, może zostać złamana przez atakującego, który biernie obserwuje wyjście.

Uwaga {[17] } - jeśli wywołujesz nextBytes() szybciej niż Twój system operacyjny jest w stanie zapisać losowe bajty(entropię) do /dev/random, to może wpaść w kłopoty podczas używania natywnego PRNG. W takim przypadku należy użyć instancji PRNG SHA1 SecureRandom i co kilka minut(lub jakiś interwał) zalać tę instancję wartością z nextBytes() natywnej instancji PRNG SecureRandom. Równoległe uruchamianie tych dwóch funkcji zapewni regularne dodawanie prawdziwych wartości losowych, a jednocześnie nie wyczerpuje entropii uzyskanej przez System operacyjny.

 64
Author: Ashwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-12 13:12:08

Jeśli uruchomisz dwa razy java.util.Random.nextLong() z tym samym nasionem, wytworzy ten sam numer. Ze względów bezpieczeństwa chcesz trzymać się java.security.SecureRandom, ponieważ jest to o wiele mniej przewidywalne.

2 klasy są podobne, myślę, że wystarczy zmienić Random na SecureRandom za pomocą narzędzia do refaktoryzacji i większość istniejącego kodu będzie działać.

 8
Author: Mualig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-15 13:10:37

Jeśli zmiana istniejącego kodu jest niedrogim zadaniem, sugeruję użycie klasy SecureRandom zgodnie z sugestią w Javadoc.

Nawet jeśli znajdziesz implementację klasy losowej używa klasy SecureRandom wewnętrznie. nie należy przyjmować za pewnik, że:

  1. inne implementacje VM robią to samo.
  2. Implementacja klasy losowej w przyszłych wersjach JDK nadal używa klasy SecureRandom

Więc lepszym wyborem jest podążanie za sugestia dokumentacji i przejść bezpośrednio z SecureRandom.

 3
Author: Andrea Parodi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-21 04:19:15

Ziarno jest bez znaczenia. Dobry generator losowy różni się wybraną liczbą pierwotną. Każdy generator losowy zaczyna się od liczby i przechodzi przez "pierścień". Co oznacza, że przechodzisz od jednej liczby do następnej, ze starą wewnętrzną wartością. Ale po chwili można dotrzeć do początku ponownie i zacząć wszystko od nowa. Więc uruchamiasz cykle. (wartość zwracana z generatora losowego nie jest wartością wewnętrzną)

Jeśli użyjesz liczby pierwszej do utworzenia pierścienia, wszystkie liczby w tym pierścieniu otrzymają wybrany, przed ukończeniem pełnego cyklu przez wszystkie możliwe liczby. Jeśli weźmiesz liczby inne niż pierwsze, nie wszystkie liczby są wybierane i otrzymujesz krótsze cykle.

Wyższe liczby pierwsze oznaczają dłuższe cykle przed powrotem do pierwszego elementu. Tak więc Bezpieczny generator losowy ma po prostu dłuższy cykl, zanim ponownie osiągnie początek, dlatego jest bezpieczniejszy. Nie można przewidzieć generowania liczb tak łatwo, jak przy krótszych cyklach.

Innymi słowy: musisz zastąpić wszystkie.

 2
Author: Nicolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-24 22:22:59

Bieżąca implementacja referencyjna java.util.Random.nextLong() wykonuje dwa wywołania do metody next(int), która bezpośrednio ujawnia 32 bit bieżącego ziarna:

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

Górne 32 bity wyniku nextLong() są bitami ziarna w czasie. Ponieważ szerokość ziarna wynosi 48 bitów (mówi javadoc), wystarczy*, aby iterację nad pozostałymi 16 bitami (to tylko 65.536 prób), aby określić ziarno, które wyprodukowało drugi 32 bit.

Gdy ziarno jest znane, wszystkie następujące żetony mogą łatwo obliczyć.

Używając wyjścia nextLong() bezpośrednio, częściowo sekret PNG do tego stopnia, że cały sekret można obliczyć z bardzo niewielkim wysiłkiem. Niebezpieczne!

* jest trochę wysiłku, jeśli drugi 32 bit jest ujemny, ale można się tego dowiedzieć.

 1
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-21 14:49:49

Postaram się użyć bardzo podstawowych słów, abyś mógł łatwo zrozumieć różnicę między Random i secureRandom a znaczeniem SecureRandom klasy.

Zastanawiałeś się kiedyś, jak generowane jest OTP (one time password)? Do wygenerowania OTP używamy również klasy Random i SecureRandom. Teraz aby twój OTP był silny, SecureRandom jest lepszy, ponieważ zajęło 2^128 spróbuj, aby złamać OTP, co jest prawie niemożliwe przez obecną maszynę, ale jeśli używana jest losowa Klasa, Twoje OTP może zostać złamane przez kogoś, kto może uszkodzić Twoje dane, ponieważ zajęło tylko 2^48 spróbuj, aby złamać.

 0
Author: sachin pathak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-11 11:06:41