Разница между java.util.Random и java.security.SecureRandom

Моя команда получила серверный код (на Java), который генерирует случайные токены, и у меня есть вопрос относительно того же самого -

Назначение этих токенов довольно чувствительно - используется для идентификатора сеанса, ссылок для сброса пароля и т. Д. Поэтому они должны быть криптографически случайными, чтобы не допустить, чтобы кто-то их угадал или применил грубую силу. Маркер является "длинным" так что это 64 бита.

Код в настоящее время используетjava.util.Random класс для генерации этих токенов. Документация ([http://docs.oracle.com/javase/7/docs/api/java/util/Random.html][1]) заjava.util.Random четко заявляет следующее:

Instances of java.util.Random are not cryptographically secure. Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications.

Тем не менее, способ, которым код в настоящее время используетjava.util.Random это - это создаетjava.security.SecureRandom класс, а затем используетSecureRandom.nextLong() метод получения семян, который используется для создания экземпляраjava.util.Randomучебный класс. Тогда он используетjava.util.Random.nextLong() метод генерации токена.

Итак, мой вопрос сейчас - это все еще небезопасно, учитывая, чтоjava.util.Random в настоящее время используетсяjava.security.SecureRandom? Нужно ли изменять код, чтобы он использовалjava.security.SecureRandom исключительно для генерации токенов?

В настоящее время кодовое полеRandom один раз при запуске

 Peter Štibraný15 июн. 2012 г., 15:08
После заполнения выводом из java.util.Random является детерминированная последовательность чисел. Вы можете не хотеть этого.
 Thorsten S.20 июн. 2012 г., 15:03
Есть еще одна серьезная проблема. 64бит означает 1,84 * 10 ^ 19 возможных комбинаций, что слишком мало, чтобы выдержать изощренную атаку. Есть машины, которые взломали 56-битный код DES (менее 256 раз) с 90 * 10 ^ 9 ключами в секунду за 60 часов. Используйте 128 бит или два длинных!
 Tom Anderson15 июн. 2012 г., 15:17
Является ли кодRandom один раз при запуске, или он запускает новый для каждого токена? Надеюсь, это глупый вопрос, но я подумал, что проверю.
 Peter Lawrey15 июн. 2012 г., 15:26
Random имеет только 48-битное внутреннее состояние и будет повторяться после 2 ^ 48 вызовов nextLong (), что означает, что он не выдаст все возможныеlong или жеdouble ценности.

Ответы на вопрос(7)

тогда как SecureRandom может иметь до 128 бит. Так что шансы на повтор в безопасном случае очень малы.

Random используетsystem clock как семя / или создать семя. Таким образом, они могут быть легко воспроизведены, если злоумышленник знает время, когда было создано семя. НоSecureRandom принимаетRandom Data от твоегоos(они могут быть интервалом между нажатиями клавиш и т. д. - большинство сборщиков данных хранят их в файлах -/dev/random and /dev/urandom in case of linux/solaris) и использует это как семя.
Поэтому, если небольшой размер токена в порядке (в случае Random), вы можете продолжать использовать свой код без каких-либо изменений, поскольку вы используете SecureRandom для генерации начального числа. Но если вы хотите больше токенов (которые не могут быть предметомbrute force attacks) перейти с SecureRandom -
В случае случайного просто2^48 попытки требуются, с современными процессорами, которые можно сломать на практике. Но для безопасного случайного2^128 Потребуются попытки, которые потребуются годы и годы, чтобы безубыточности с современными машинами.

Увидеть этот ссылка для более подробной информации.
EDIT
После прочтения ссылок, предоставленных @emboss, становится ясно, что семя, каким бы случайным оно ни было, не должен использоваться с java.util.Random. Очень легко рассчитать начальное значение, наблюдая за выходными данными.

Go for SecureRandom - использоватьNative PRNG (как указано в ссылке выше), потому что он принимает случайные значения из/dev/random файл для каждого звонкаnextBytes(), Таким образом, злоумышленник, наблюдающий за выходом, не сможет ничего разобрать, если он не контролирует содержимое/dev/random файл (что очень маловероятно)
sha1 prng Алгоритм вычисляет начальное число только один раз, и если ваша виртуальная машина работает в течение нескольких месяцев с использованием одного и того же начального числа, она может быть взломана злоумышленником, который пассивно наблюдает за выходными данными.

NOTE - Если вы звонитеnextBytes() быстрее, чем ваша ОС в состоянии записать случайные байты (энтропию) в/dev/random, вы можете попасть в беду при использованииNATIVE PRNG, В этом случае используйте экземпляр SecureRandom SHA1 PRNG и каждые несколько минут (или некоторый интервал) заполняйте этот экземпляр значением изnextBytes() НАТУРАЛЬНОГО экземпляра PRNG SecureRandom. Параллельный запуск этих двух параметров гарантирует, что вы будете регулярно сеять с истинными случайными значениями, а также не исчерпывает энтропию, полученную операционной системой.

 11 июн. 2015 г., 23:04
Наш файл java.security содержал securerandom.source = file: / dev / urandom вместо file: /// dev / urandom (две косые черты после двоеточия для файлового протокола, затем еще одна косая черта для корня файловой системы), что привело к его откату в / dev / random, что вызвало проблемы с исчерпанием энтропийного пула. Не удалось отредактировать его, поэтому необходимо установить системное свойство java.security.egd на правильное значение при запуске приложения.
 24 июн. 2012 г., 13:56
Будьте осторожны с Linux: он может достигнуть исчерпания энтропии (больше в ВМ, чем с аппаратным обеспечением)! смотреть на/proc/sys/kernel/random/entropy_avail и проверить с некоторыми дампами потока, что нет, слишком долго ждать при чтении на/dev/random
 06 авг. 2014 г., 17:45
Обратите внимание, что Oracle JRE (как минимум 1.7) по умолчанию работает с / dev / urandom, а не с / dev / random, поэтому суффикс вашего ответа больше не верен. проверить проверку $ JAVA_HOME / lib / security / java.security для свойства securerandom.source
 16 июн. 2012 г., 18:18
@ emboss: я говорю о грубой силе.
 16 июн. 2012 г., 14:46
Требуется намного меньше, чем 2 ^ 48, чтобы предсказатьRandomOP не должен использоватьRandom совсем.

java.util.Random.nextLong() делает два вызова методаnext(int) которыйdirectly выставляет 32 бита текущего начального числа:

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

Верхний 32-битный результатnextLong() биты семени в то время. Поскольку ширина начального числа равна 48 битам (говорит Javadoc), достаточно * перебрать оставшиеся 16-битные (то есть всего 65,536 попыток), чтобы определить начальное число, которое произвело вторые 32-битные значения.

Как только семя известно, все последующие токены могут быть легко вычислены.

Using the output of nextLong() directly, partly the secret of the PNG to a degree that the entire secret can be computed with very little efford. Dangerous!

* Необходимы некоторые усилия, если вторые 32 бита отрицательны, но это можно выяснить.

 06 янв. 2014 г., 21:34
Правильный. Посмотрите, как быстро взломать java.util.random вjazzy.id.au/default/2010/09/20/… !

я предлагаю вам использовать класс SecureRandom, как это предлагается в Javadoc.

Даже если вы обнаружите, что реализация класса Random внутренне использует класс SecureRandom. Вы не должны принимать это как должное, что:

Other VM implementations do the same thing. Implementation of the Random class in future versions of the JDK still use the SecureRandom class

Так что лучше следовать рекомендациям по документации и перейти непосредственно к SecureRandom.

 15 июн. 2012 г., 15:17
Я не верю, что в первоначальном вопросе говорилось, чтоjava.util.Random реализация используетсяSecureRandom внутренне сказаноtheir code использованияSecureRandom сеятьRandom, Тем не менее, я согласен с обоими ответами до сих пор; лучше всего использоватьSecureRandom чтобы избежать явно детерминированного решения.
 15 июн. 2012 г., 15:50
О да. Моя вина...

Я постараюсь использовать самые простые слова, чтобы вы могли легко понять разницу между Random и secureRandom и важность класса SecureRandom.

Вы никогда не задумывались, как генерируется OTP (одноразовый пароль)? Для генерации OTP мы также используем класс Random и SecureRandom. Теперь, чтобы сделать ваш OTP сильным, SecureRandom лучше, потому что потребовалось 2 ^ 128 попыток, чтобы взломать OTP, что практически невозможно на нынешней машине, но если используется Случайный класс, тогда ваш OTP может быть взломан кем-то, кто может повредить ваши данные, потому что просто 2 ^ 48 попробуй, взломать.

Семя бессмысленно. Хороший генератор случайных чисел отличается выбранным основным номером. Каждый генератор случайных чисел начинается с числа и проходит через «кольцо». Это означает, что вы переходите от одного числа к другому со старым внутренним значением. Но через некоторое время вы достигаете начала снова и начинаете все сначала. Итак, вы запускаете циклы. (возвращаемое значение от случайного генератора не является внутренним значением)

Если вы используете простое число для создания кольца, выбираются все числа в этом кольце, прежде чем завершить полный цикл по всем возможным числам. Если вы берете не простые числа, выбираются не все числа, и вы получаете более короткие циклы.

Чем выше простые числа, тем больше циклов, прежде чем вы снова вернетесь к первому элементу. Таким образом, безопасный генератор случайных чисел просто имеет более длинный цикл, прежде чем снова достичь начала, поэтому он безопаснее. Вы не можете предсказать генерацию чисел так же просто, как с более короткими циклами.

Другими словами: вы должны заменить все.

java.util.Random.nextLong() с тем же семенем, он будет производить то же число. По соображениям безопасности вы хотите придерживатьсяjava.security.SecureRandom потому что это намного менее предсказуемо.

2 класса похожи, я думаю, вам просто нужно изменитьRandom вSecureRandom с инструментом рефакторинга, и большая часть вашего существующего кода будет работать.

 15 июн. 2012 г., 15:48
Если вы берете два экземпляра любого PRNG и заполняете его одним и тем же значением, вы всегда получаете одни и те же случайные числа, даже использование SecureRandom не меняет этого. Все PRNG являются детерминированными и, следовательно, предсказуемыми, если вы знаете семя.
 15 июн. 2012 г., 16:24
Существуют разные реализации SecureRandom, некоторые являются PRNG, некоторые нет. С другой стороны, java.util.Random всегда является PRNG (как определено в его Javadoc).
Решение Вопроса

конгруэнтный генератор для получения случайных значений вjava.util.Random.

Взято изjava.util.Random исходный код (JDK 7u2), из комментария к методуprotected int next(int bits), который генерирует случайные значения:

This is a linear congruential pseudorandom number generator, as defined by D. H. Lehmer and described by Donald E. Knuth in The Art of Computer Programming, Volume 3: Seminumerical Algorithms, section 3.2.1.

Predictability of Linear Congruential Generators

Хьюго Кравчик (Hugo Krawczyk) написал довольно хорошую статью о том, как можно прогнозировать эти LCG («Как прогнозировать конгруэнтные генераторы»). Если вам повезет и вы заинтересуетесь, вы все равно сможете найти бесплатную загружаемую версию в Интернете. И есть еще много исследований, которые ясно показывают, что вы должныnever использовать LCG в целях безопасности. Это также означает, что ваши случайные числаare прямо сейчас, то, что вам не нужно для идентификаторов сеансов и тому подобного.

How to break a Linear Congruential Generator

Предположение о том, что злоумышленнику придется ждать повторения LCG после полного цикла, неверно. Даже с оптимальным циклом (модуль m в его рекуррентном соотношении) очень легко предсказать будущие значения за гораздо меньшее время, чем полный цикл. В конце концов, это просто набор модульных уравнений, которые необходимо решить, что становится легко, как только вы наблюдаете достаточно выходных значений LCG.

Безопасность не улучшается с «лучше» семена. Это просто не имеет значения, если вы посеете случайное значение, сгенерированноеSecureRandom или даже получить значение, бросая кубик несколько раз.

Злоумышленник просто вычислит начальное значение из наблюдаемых выходных значений. Это занимаетsignificantly less время, чем 2 ^ 48 в случаеjava.util.Random, Неверующие могут попробовать этоэкспериментгде показано, что вы можете предсказать будущееRandom выводит, соблюдая только два (!) выходных значения во времени примерно 2 ^ 16. На современном компьютере не требуется даже секунды, чтобы предсказать вывод ваших случайных чисел прямо сейчас.

Conclusion

Замените свой текущий код. использованиеSecureRandom исключительно. Тогда, по крайней мере, у вас будет небольшая гарантия того, что результат будет трудно предсказать. Если вы хотите получить свойства криптографически защищенного PRNG (в вашем случае это то, что вам нужно), тогда вам нужно идти сSecureRandom только. Умение менять способ его использования почти всегда приводит к чему-то менее безопасному ...

 18 июн. 2012 г., 13:41
Очень полезно, может быть, вы могли бы также объяснить, как работает SecureRandom (так же, как вы объясняете, как работает Random) ..
 18 июн. 2012 г., 13:46
Это побеждает цель secureRandom
 23 июн. 2012 г., 18:42
@JoelCoehoorn Это не такRandom сломан - его следует использовать только в разных сценариях. Конечно, вы всегда можете использовать SecureRandom. А вообще,SecureRandom заметно медленнее, чем чистыйRandom, И есть случаи, когда вас интересуют только хорошие статистические свойства и отличная производительность, но вы не заботитесь о безопасности: симуляции Монте-Карло являются хорошим примером. Я сделал комментарии об этом вsimilar answerможет быть, вы найдете это полезным.
 18 июн. 2012 г., 13:53
@azulflame безопасность через неизвестность редко работает
 23 июн. 2012 г., 00:45
Настоящий вопрос здесь: если java может создавать более безопасный prng с аналогичным API, почему он просто не заменит сломанный?

Ваш ответ на вопрос