Разница между Java.утиль.Случайные и java.безопасность.SecureRandom



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



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



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




экземпляры java.утиль.Случайные не являются криптографически безопасными. Вместо этого рассмотрите возможность использования 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 один раз при запуске

639   7  

7 ответов:

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

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

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

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

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

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

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

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

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

вывод

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.