Я искал простой алгоритм Java для генерации псевдослучайной буквенно-цифровой строки. В моей ситуации он будет использоваться в качестве уникального идентификатора сеанса / ключа, который «вероятно» будет уникальным для 500K+
поколения (мои потребности на самом деле не требуют ничего более сложного).
В идеале я мог бы указать длину в зависимости от моих потребностей уникальности. Например, сгенерированная строка длиной 12 может выглядеть примерно так "AEYGF7K0DM1X"
.
java
string
random
alphanumeric
Тодд
источник
источник
Long.toHexString(Double.doubleToLongBits(Math.random()));
UUID.randomUUID().toString();
RandomStringUtils.randomAlphanumeric(12);
Ответы:
Алгоритм
Чтобы сгенерировать случайную строку, объедините символы, нарисованные случайным образом из набора допустимых символов, пока строка не достигнет желаемой длины.
Реализация
Вот несколько довольно простых и очень гибких кодов для генерации случайных идентификаторов. Прочитайте информацию, которая следует для важных замечаний по применению.
Примеры использования
Создайте небезопасный генератор для 8-символьных идентификаторов:
Создайте безопасный генератор для идентификаторов сессии:
Создайте генератор с удобочитаемыми кодами для печати. Строки длиннее буквенно-цифровых строк, чтобы компенсировать использование меньшего количества символов:
Использовать в качестве идентификаторов сессии
Генерировать идентификаторы сеанса, которые могут быть уникальными, недостаточно, или вы можете просто использовать простой счетчик. Злоумышленники перехватывают сессии, когда используются предсказуемые идентификаторы.
Существует напряженность между длиной и безопасностью. Более короткие идентификаторы легче угадать, потому что возможностей меньше. Но более длинные идентификаторы потребляют больше памяти и пропускной способности. Помогает больший набор символов, но он может вызвать проблемы с кодированием, если идентификаторы включены в URL-адреса или введены вручную.
Основной источник случайности или энтропии для идентификаторов сеансов должен исходить от генератора случайных чисел, предназначенного для криптографии. Однако инициализация этих генераторов иногда может быть вычислительно дорогой или медленной, поэтому следует предпринять усилия для их повторного использования, когда это возможно.
Использовать в качестве идентификаторов объектов
Не каждое приложение требует безопасности. Случайное назначение может быть эффективным способом для нескольких объектов генерировать идентификаторы в совместно используемом пространстве без какой-либо координации или разделения. Координация может быть медленной, особенно в кластерной или распределенной среде, и разделение пространства вызывает проблемы, когда объекты в конечном итоге получают слишком маленькие или слишком большие ресурсы.
Идентификаторы, созданные без принятия мер по их непредсказуемости, должны быть защищены другими средствами, если злоумышленник сможет их просматривать и манипулировать, как это происходит в большинстве веб-приложений. Должна быть отдельная система авторизации, которая защищает объекты, чей идентификатор может быть угадан злоумышленником без разрешения доступа.
Также следует позаботиться о том, чтобы использовать идентификаторы, достаточно длинные, чтобы сделать коллизии маловероятными, учитывая ожидаемое общее количество идентификаторов. Это называется «парадоксом дня рождения». Вероятность столкновения, p , приблизительно равна n 2 / (2q x ), где n - количество фактически сгенерированных идентификаторов, q - количество различных символов в алфавите, а x - длина идентификаторов. Это должно быть очень маленькое число, например, 2-50 или меньше.
Выработка этого показывает, что вероятность столкновения между 500k 15-символьными идентификаторами составляет около 2–52 , что, вероятно, менее вероятно, чем необнаруженные ошибки космических лучей и т. Д.
Сравнение с UUID
Согласно их спецификации UUID не предназначены для непредсказуемости и не должны использоваться в качестве идентификаторов сеанса.
UUID в их стандартном формате занимают много места: 36 символов только для 122 бит энтропии. (Не все биты «случайного» UUID выбираются случайным образом.) Случайно выбранная буквенно-цифровая строка упаковывает больше энтропии всего в 21 символ.
UUID не являются гибкими; они имеют стандартизированную структуру и расположение. Это их главное достоинство, а также их главная слабость. При сотрудничестве с внешней стороной может оказаться полезной стандартизация, предлагаемая UUID. Для чисто внутреннего использования они могут быть неэффективными.
источник
.replaceAll("\\d", " ");
присоединиться к концуreturn new BigInteger(130, random).toString(32);
строки, чтобы выполнить обмен регулярными выражениями. Он заменяет все цифры пробелами. Прекрасно работает для меня: я использую это вместоsymbols
и используя вместо них пробел; Вы можете контролировать среднюю длину слова, изменяя количество пробелов в символах (больше случаев для более коротких слов). Для действительно чрезмерного поддельного текстового решения вы можете использовать цепочку Маркова!SecureRandom
экземпляром, назначеннымrandom
переменной.Java предоставляет способ сделать это напрямую. Если вы не хотите тире, их легко удалить. Просто используйте
uuid.replace("-", "")
Вывод:
источник
UUID.randomUUID().toString().replaceAll("-", "");
делает строку буквенно-цифровой, как требуется.источник
SecureRandom
вместоRandom
класса. Если пароли генерируются на сервере, он может быть уязвим для временных атак.AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
и некоторые другие разрешенные символы.static Random rnd = new Random();
внутрь метода?Random
объекта в каждом вызове метода? Я так не думаю.Если вы счастливы использовать классы Apache, вы можете использовать
org.apache.commons.text.RandomStringGenerator
(commons-text).Пример:
Так как commons-lang 3.6,
RandomStringUtils
устарела.источник
Apache Commons Lang 3.3.1
библиотеки - и он использует толькоjava.util.Random
для обеспечения случайных последовательностей, так что производят небезопасную последовательность .public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Для этого вы можете использовать библиотеку Apache: RandomStringUtils
источник
compile 'commons-lang:commons-lang:2.6'
SecureRandom
и вы хорошо.В одну строку:
источник
AEYGF7K0DM1X
который не является шестнадцатеричным. Меня беспокоит, как часто люди ошибочно принимают буквенно-цифровые и шестнадцатеричные числа. Они не одно и то же.Math.random()
создает значениеdouble
от 0 до 1, поэтому часть экспоненты в основном не используется. Используйтеrandom.nextLong
для случайногоlong
вместо этого уродливого взлома.Это легко достижимо без каких-либо внешних библиотек.
1. Криптографическая генерация псевдослучайных данных
Сначала вам нужен криптографический PRNG. Java имеет
SecureRandom
для этого и обычно использует лучший источник энтропии на машине (например/dev/random
). Узнайте больше здесь.Замечания:
SecureRandom
это самый медленный, но самый безопасный способ в Java генерации случайных байтов. Однако я рекомендую НЕ рассматривать производительность здесь, поскольку она обычно не оказывает реального влияния на ваше приложение, если вам не нужно генерировать миллионы токенов в секунду.2. Требуемое пространство возможных значений
Затем вы должны решить, насколько уникальным должен быть ваш токен. Единственный смысл рассмотрения энтропии - убедиться, что система может противостоять атакам грубой силы: пространство возможных значений должно быть настолько большим, что любой злоумышленник может попробовать пренебрежимо малую долю значений в несмешное время 1 . Уникальные идентификаторы, такие как random,
UUID
имеют 122-битную энтропию (т. Е. 2 ^ 122 = 5,3x10 ^ 36) - вероятность столкновения равна "* (...), так как вероятность дублирования равна 1 на миллиард, версия 103 триллионов 4 UUID должны быть сгенерированы 2 ". Мы выберем 128 бит, так как он вписывается ровно в 16 байтов и считается достаточнымза то, что они уникальны для каждого, но наиболее экстремального варианта использования, и вам не нужно думать о дубликатах. Вот простая таблица сравнения энтропии, включая простой анализ проблемы дня рождения .Для простых требований может быть достаточно 8 или 12 байтов, но с 16 байтами вы на «безопасной стороне».
И это в основном все. Последнее, что нужно подумать о кодировании, чтобы его можно было представить в виде печатного текста (читай, а
String
).3. Бинарное в текстовое кодирование
Типичные кодировки включают в себя:
Base64
каждый символ кодирует 6 бит, создавая 33% накладных расходов. К счастью, есть стандартные реализации в Java 8+ и Android . С более старой Java вы можете использовать любую из многочисленных сторонних библиотек . Если вы хотите, чтобы ваши токены были безопасными для URL, используйте безопасную для URL версию RFC4648 (которая обычно поддерживается большинством реализаций). Пример кодирования 16 байтов с заполнением:XfJhfv3C0P6ag7y9VQxSbw==
Base32
каждый символ кодирует 5 бит, создавая 40% накладных расходов. Это позволит использовать егоA-Z
и2-7
сделать его разумно эффективным с учетом букв и цифр без учета регистра. В JDK нет стандартной реализации . Пример кодирования 16 байтов без заполнения:WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16
(шестнадцатеричный) каждый символ кодирует 4 бита, требуя 2 символа на байт (т.е. 16 байт создают строку длиной 32). Поэтому hex меньше места,Base32
но безопаснее в большинстве случаев (url), поскольку он использует только0-9
иA
toF
. Пример кодирования 16 байт:4fa3dd0f57cb3bf331441ed285b27735
. Смотрите SO обсуждение о преобразовании в hex здесь.Дополнительные кодировки, такие как Base85 и экзотическая Base122, существуют с лучшей / худшей эффективностью пространства. Вы можете создать свою собственную кодировку (что обычно делают большинство ответов в этой теме), но я бы посоветовал против этого, если у вас нет особых требований. Смотрите больше схем кодирования в статье Википедии.
4. Резюме и пример
SecureRandom
hex
илиbase32
если вам нужно, чтобы он был буквенно-цифровым)не
Пример: Генератор шестнадцатеричных токенов
Пример: Base64 Token Generator (безопасный URL)
Пример: Java CLI Tool
Если вам нужен готовый инструмент cli, вы можете использовать игру в кости: https://github.com/patrickfav/dice
Пример: Связанная проблема - Защитите свои текущие идентификаторы
Если у вас уже есть идентификатор, который вы можете использовать (например, синтетический
long
в вашей сущности), но не хотите публиковать внутреннее значение , вы можете использовать эту библиотеку для его шифрования и обфускации: https://github.com/patrickfav / ID-маскаисточник
BigInteger
s, используя параметр конструктора:BigInteger(1, token)
вместоBigInteger(token)
.import java.security.SecureRandom;
иimport java.math.BigInteger;
нужны, чтобы пример работал, но он прекрасно работает!new SecureRandom()
использует/dev/urandom
Использование доллара должно быть простым, как:
это выводит что-то вроде этого:
источник
Вот это на Java:
Вот пример прогона:
источник
Random#nextInt
илиnextLong
. Переключитесь наSecureRandom
при необходимости.Удивительно, что никто здесь не предложил это, но:
Легко.
Преимущество этого в том, что UUID красивы и длинны и гарантированно почти невозможны для столкновения.
В Википедии есть хорошее объяснение этого:
http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
Первые 4 бита являются типом версии и 2 для варианта, поэтому вы получаете 122 бита случайным образом. Так что если вы хотите, вы можете обрезать с конца, чтобы уменьшить размер UUID. Это не рекомендуется, но у вас все еще есть множество случайностей, достаточно для ваших 500k записей легко.
источник
Краткое и простое решение, но с использованием только строчных и цифровых символов:
Размер составляет около 12 цифр для базы 36 и не может быть улучшен таким образом. Конечно, вы можете добавить несколько экземпляров.
источник
Long.toString(Math.abs(r.nextLong()), 36);
abs
решается с помощью побитового оператора для сброса наиболее значимого бита. Это будет работать для всех значений.<< 1 >>> 1
.Альтернатива в Java 8:
источник
Использование UUID небезопасно, потому что части UUID вообще не случайны. Процедура @erickson очень аккуратна, но не создает строки одинаковой длины. Следующий фрагмент должен быть достаточным:
Почему выбирают
length*5
. Давайте предположим простой случай случайной строки длиной 1, поэтому один случайный символ. Чтобы получить случайный символ, содержащий все цифры 0-9 и символы az, нам понадобится случайное число от 0 до 35, чтобы получить по одному каждому символу.BigInteger
предоставляет конструктор для генерации случайного числа, равномерно распределенного по диапазону0 to (2^numBits - 1)
. К сожалению, 35 - это не число, которое может быть получено 2 ^ numBits - 1. Таким образом, у нас есть два варианта: либо пойти с2^5-1=31
или2^6-1=63
. Если бы мы выбрали,2^6
мы получили бы много «ненужных» / «длинных» номеров. Поэтому2^5
это лучший вариант, даже если мы потеряем 4 символа (wz). Чтобы теперь сгенерировать строку определенной длины, мы можем просто использовать2^(length*numBits)-1
число. Последняя проблема: если нам нужна строка определенной длины, случайное число может сгенерировать небольшое число, поэтому длина не будет достигнута, поэтому мы должны дополнить строку до требуемой длины предваряющими нулями.источник
источник
Так что это просто добавляет пароль в строку и ... да, хорошо работает, проверь это ... очень просто. я это написал
источник
+ 0
это часто? Почему вы разделяете объявление о месте и инициализации? В чем преимущество индексов 1,2,3,4 вместо 0,1,2,3? Самое главное: вы взяли случайное значение и сравнили с новым значением 4 раза, которое всегда могло не совпадать, не получая больше случайности. Но не стесняйтесь откат.Я нашел это решение, которое генерирует случайную строку в шестнадцатеричном формате. Предоставленный модульный тест, кажется, соответствует моему основному варианту использования. Хотя это немного сложнее, чем некоторые другие ответы.
источник
Измените строковые символы в соответствии с вашими требованиями.
Строка неизменна. Это
StringBuilder.append
более эффективно, чем конкатенация строк.источник
Random
экземпляра в каждой итерации цикла неэффективно.источник
источник
Не очень нравится любой из этих ответов относительно "простого" решения: S
Я хотел бы пойти на простой;), чистый Java, один вкладыш (энтропия основана на случайной длине строки и заданном наборе символов):
или (немного более читаемый старый способ)
Но с другой стороны, вы также можете использовать UUID с довольно хорошей энтропией ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):
Надеюсь, это поможет.
источник
Вы упоминаете «простой», но на всякий случай, если кто-то еще ищет что-то, отвечающее более строгим требованиям безопасности, вы можете взглянуть на jpwgen . jpwgen смоделирован после pwgen в Unix и очень настраивается.
источник
Вы можете использовать класс UUID с его сообщением getLeastSignificantBits (), чтобы получить 64-битные данные Random, а затем преобразовать их в число с основанием 36 (то есть строку, состоящую из 0-9, AZ):
Это дает строку длиной до 13 символов. Мы используем Math.abs (), чтобы убедиться, что в нем нет пробивающегося знака минус.
источник
random.nextLong()
? Или дажеDouble.doubleToLongBits(Math.random())
?Вы можете использовать следующий код, если ваш пароль обязательно содержит цифры буквенных специальных символов:
источник
Вот однострочный код от AbacusUtil
Случайный не означает, что он должен быть уникальным. чтобы получить уникальные строки, используя:
источник
Вот это решение Scala:
источник
с помощью библиотеки apache это можно сделать в одну строку
вот документ http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html
источник
источник
Я думаю, что это самое маленькое решение здесь или почти одно из самых маленьких:
Код работает просто отлично. Если вы используете этот метод, я рекомендую использовать более 10 символов. Столкновение происходит при 5 символах / 30362 итерации. Это заняло 9 секунд.
источник
источник
length
вместоchars.length
цикла for:for (int i = 0; i < length; i++)
источник