Разница между 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 один раз при запуске
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требуются попытки, с сегодняшними продвинутыми процессорами можно сломать его в практическом времени. Но для securerandom2^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 внутренне. вы не должны принимать это как должное, что:
- другие реализации VM делают то же самое.
- реализация случайного класса в будущих версиях 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