Моя команда получила серверный код (на Java), который генерирует случайные токены, и у меня есть вопрос относительно того же самого -
Назначение этих токенов довольно чувствительно - используется для идентификатора сеанса, ссылок для сброса пароля и т. Д. Поэтому они должны быть криптографически случайными, чтобы не допустить, чтобы кто-то их угадал или применил грубую силу. Токен является «длинным», поэтому он имеет длину 64 бита.
Код в настоящее время использует java.util.Random
класс для генерации этих токенов. Документация для java.util.Random
четко говорится следующее:
Экземпляры java.util.Random не являются криптографически безопасными. Вместо этого рассмотрите возможность использования SecureRandom для получения криптографически безопасного генератора псевдослучайных чисел для использования чувствительными к безопасности приложениями.
Однако в настоящее время код использует 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
единственным при запуске
источник
Random
один раз при запуске или новый для каждого токена? Надеюсь, это глупый вопрос, но я решил проверить.long
илиdouble
значения.Ответы:
Стандартная реализация Oracle JDK 7 использует так называемый линейный конгруэнтный генератор для генерации случайных значений
java.util.Random
.Взято из
java.util.Random
исходного кода (JDK 7u2), из комментария к методуprotected int next(int bits)
, который генерирует случайные значения:Предсказуемость линейных конгруэнтных генераторов
Хьюго Кравчик написал довольно хорошую статью о том, как можно прогнозировать эти LCG («Как прогнозировать конгруэнтные генераторы»). Если вам повезет и вы заинтересуетесь, вы все равно можете найти бесплатную загружаемую версию в Интернете. И есть еще много исследований, которые ясно показывают, что вы никогда не должны использовать LCG в целях безопасности. Это также означает , что ваши случайные числа имеют предсказуемый прямо сейчас, что вы не хотите для идентификаторов сеансов и тому подобное.
Как сломать линейный конгруэнтный генератор
Предположение, что злоумышленнику придется ждать повторения LCG после полного цикла, неверно. Даже при оптимальном цикле (модуль m в его рекуррентном соотношении) очень легко предсказать будущие значения за гораздо меньшее время, чем полный цикл. В конце концов, это просто набор модульных уравнений, которые необходимо решить, что становится легко, как только вы наблюдаете достаточно выходных значений LCG.
Безопасность не улучшается с «лучшим» семенем. Это просто не имеет значения, если вы посеете случайное значение, сгенерированное
SecureRandom
или даже произведенное, бросая кубик несколько раз.Злоумышленник просто вычислит начальное значение из наблюдаемых выходных значений. Это занимает значительно меньше времени, чем 2 ^ 48 в случае
java.util.Random
. Неверующие могут попробовать этот эксперимент , где показано, что вы можете предсказать будущееRandom
результаты, наблюдая только два (!) Выходных значения во времени примерно 2 ^ 16. На современном компьютере не требуется даже секунды, чтобы предсказать вывод ваших случайных чисел прямо сейчас.Вывод
Замените свой текущий код. Используйте
SecureRandom
исключительно. Тогда, по крайней мере, у вас будет небольшая гарантия того, что результат будет трудно предсказать. Если вам нужны свойства криптографически защищенного PRNG (в вашем случае это то, что вы хотите), то вам нужно только идтиSecureRandom
. Умение менять способ его использования почти всегда приведет к чему-то менее безопасному ...источник
Random
сломано - его просто нужно использовать в разных сценариях. Конечно, вы всегда можете использовать SecureRandom. Но в целомSecureRandom
заметно медленнее, чем чистыйRandom
. И есть случаи, когда вас интересуют только хорошие статистические свойства и отличная производительность, но вы не очень заботитесь о безопасности: моделирование по методу Монте-Карло является хорошим примером. Я комментировал это в похожем ответе , может быть, вы найдете его полезным.Случайный имеет только 48 бит, тогда как 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
будут необходимы попытки, которые потребуют годы и годы, чтобы обойтись даже с современными современными машинами.Смотрите эту ссылку для более подробной информации.
РЕДАКТИРОВАТЬ
После прочтения ссылок, предоставленных @emboss, становится ясно, что семя, каким бы случайным оно ни было, не должно использоваться с java.util.Random. Очень легко рассчитать начальное значение, наблюдая за выходными данными.
Перейти на SecureRandom - использовать собственный PRNG (как указано в ссылке выше), потому что он принимает случайные значения из
/dev/random
файла для каждого вызоваnextBytes()
, Таким образом, злоумышленник, наблюдающий за выводом, не сможет ничего разобрать, если он не контролирует содержимое/dev/random
файла (что очень маловероятно). Алгоритм sha1 prng вычисляет начальное значение только один раз, и если ваша виртуальная машина работает в течение нескольких месяцев, используя то же самое seed, он может быть взломан атакующим, который пассивно наблюдает за выходом.
ПРИМЕЧАНИЕ. - Если вы вызываете
nextBytes()
быстрее, чем ваша ОС способна записать случайные байты (энтропию) в/dev/random
, вы можете столкнуться с проблемами при использовании NATIVE PRNG . В этом случае используйте экземпляр SecureRandom SHA1 PRNG и каждые несколько минут (или некоторый интервал) заполняйте этот экземпляр значением изnextBytes()
НАТУРАЛЬНОГО экземпляра PRNG SecureRandom. Параллельный запуск этих двух параметров гарантирует, что вы будете регулярно сеять с истинными случайными значениями, а также не исчерпывает энтропию, полученную операционной системой.источник
Random
, OP не должен использоватьRandom
вообще./proc/sys/kernel/random/entropy_avail
и проверьте с некоторыми дампами потока, что нет слишком долгого ожидания при чтении на/dev/random
Если вы бежите дважды
java.util.Random.nextLong()
с одним и тем же семенем, оно даст одинаковое число. По соображениям безопасности вы хотите придерживаться,java.security.SecureRandom
потому что это гораздо менее предсказуемо.2 класса похожи, я думаю, вам просто нужно перейти
Random
наSecureRandom
инструмент рефакторинга, и большая часть вашего существующего кода будет работать.источник
Если изменение существующего кода является доступной задачей, я предлагаю вам использовать класс SecureRandom, как это предлагается в Javadoc.
Даже если вы обнаружите, что реализация класса Random внутренне использует класс SecureRandom. Вы не должны принимать это как должное, что:
Поэтому лучше следовать рекомендациям по документации и перейти непосредственно к SecureRandom.
источник
java.util.Random
реализация используетсяSecureRandom
внутренне, в нем говорится, что их код используетSecureRandom
для заполненияRandom
. Тем не менее, я согласен с обоими ответами до сих пор; Лучше всего использовать,SecureRandom
чтобы избежать явно детерминированного решения.Текущая эталонная реализация
java.util.Random.nextLong()
делает два вызова метода,next(int)
который непосредственно предоставляет 32-битный текущий разряд:Старшие 32 бита результата
nextLong()
являются битами начального числа в то время. Поскольку ширина начального числа равна 48 битам (говорит javadoc), достаточно * перебрать оставшиеся 16-битные значения (это всего лишь 65,536 попыток), чтобы определить начальное число, которое произвело вторые 32-битные значения.Как только семя известно, все последующие токены могут быть легко вычислены.
Используя вывод
nextLong()
непосредственно, частично секрета PNG до такой степени, что весь секрет может быть вычислен с очень небольшим усилием. Опасные!* Необходимы некоторые усилия, если вторые 32 бита отрицательны, но это можно выяснить.
источник
Семя бессмысленно. Хороший генератор случайных чисел отличается выбранным основным номером. Каждый генератор случайных чисел начинается с числа и проходит через «кольцо». Это означает, что вы переходите от одного числа к другому со старым внутренним значением. Но через некоторое время вы достигаете начала снова и начинаете все сначала. Итак, вы запускаете циклы. (возвращаемое значение от случайного генератора не является внутренним значением)
Если вы используете простое число для создания кольца, выбираются все числа в этом кольце, прежде чем завершить полный цикл по всем возможным числам. Если вы берете не простые числа, выбираются не все числа, и вы получаете более короткие циклы.
Чем выше простые числа, тем больше циклов, прежде чем вы снова вернетесь к первому элементу. Таким образом, безопасный генератор случайных чисел просто имеет более длинный цикл, прежде чем снова достичь начала, поэтому он безопаснее. Вы не можете предсказать генерацию чисел так же просто, как с более короткими циклами.
Другими словами: вы должны заменить все.
источник
Я постараюсь использовать самые простые слова, чтобы вы могли легко понять разницу между Random и secureRandom и важность класса SecureRandom.
Вы никогда не задумывались, как генерируется OTP (одноразовый пароль)? Для генерации OTP мы также используем класс Random и SecureRandom. Теперь, чтобы сделать ваш OTP сильным, SecureRandom лучше, потому что потребовалось 2 ^ 128 попыток, чтобы взломать OTP, что практически невозможно на нынешней машине, но если используется Случайный класс, тогда ваш OTP может быть взломан кем-то, кто может повредить ваши данные, потому что он потребовал просто 2 ^ 48 попробуй, взломать.
источник