it-swarm.com.ru

Разница между 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 четко гласит следующее: 

Экземпляры Java.util.Random не являются криптографически безопасными. Вместо этого рассмотрите возможность использования SecureRandom для получения криптографически безопасного генератора псевдослучайных чисел для использования чувствительными к безопасности приложениями.

Однако способ, которым код в настоящее время использует Java.util.Random, заключается в следующем: он создает экземпляр класса Java.security.SecureRandom, а затем использует метод SecureRandom.nextLong() для получения начального числа, которое используется для создания экземпляра класса Java.util.Randomclass. Затем он использует метод Java.util.Random.nextLong() для генерации токена.

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

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

192
user967973

Стандартная реализация Oracle JDK 7 использует так называемый линейный конгруэнтный генератор для генерации случайных значений в Java.util.Random

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

Это линейный конгруэнтный генератор псевдослучайных чисел, как определенный Д. Х. Лемером и описанный Дональдом Э. Кнутом в Искусство компьютерного программирования, Том 3: Получисленные алгоритмы, раздел 3.2.1.

Предсказуемость линейных конгруэнтных генераторов

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

Как сломать линейный конгруэнтный генератор

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

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

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

Заключение

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

222
emboss

Случайный имеет только 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 - используйте Native PRNG (как указано в приведенной выше ссылке), поскольку он принимает случайные значения из файла /dev/random для каждого вызова nextBytes(). Таким образом, злоумышленник, наблюдающий за выводом, не сможет ничего разобрать, если он не контролирует содержимое файла /dev/random (что очень маловероятно) 
Алгоритм sha1 prng вычисляет начальное число только один раз, и если ваш VM работает в течение нескольких месяцев, используя одно и то же начальное число, он может быть взломан злоумышленником, который пассивно наблюдает за выходными данными.

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

68
Ashwin

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

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

8
Mualig

Если изменение существующего кода является доступной задачей, я предлагаю вам использовать класс SecureRandom, как это предлагается в Javadoc. 

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

  1. Другие реализации VM делают то же самое. 
  2. Реализация класса Random в будущих версиях JDK все еще использует класс SecureRandom 

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

3
Andrea Parodi

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

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

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

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

2
Nicolas

Текущая эталонная реализация Java.util.Random.nextLong() делает два вызова метода next(int), который direct предоставляет 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-разрядный код.

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

Используя вывод nextLong() напрямую, частично секрет PNG до такой степени, что весь секрет может быть вычислен с минимальными затратами времени. Опасные!

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

1
Matt

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

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

0
sachin pathak