Я использую эту строку для создания идентификатора sha1 для node.js:
crypto.createHash('sha1').digest('hex');
Проблема в том, что он каждый раз возвращает один и тот же идентификатор.
Возможно ли, чтобы он каждый раз генерировал случайный идентификатор, чтобы я мог использовать его в качестве идентификатора документа базы данных?
Ответы:
Посмотрите здесь: Как использовать node.js Crypto для создания хэша HMAC-SHA1? Я бы создал хэш текущей метки времени + случайное число, чтобы гарантировать уникальность хеша:
var current_date = (new Date()).valueOf().toString(); var random = Math.random().toString(); crypto.createHash('sha1').update(current_date + random).digest('hex');
источник
243,583,606,221,817,150,598,111,409x больше энтропии
Я бы рекомендовал использовать crypto.randomBytes . Это не так
sha1
, но для целей идентификации это быстрее и так же "случайно".var id = crypto.randomBytes(20).toString('hex'); //=> f26d60305dae929ef8640a75e70dd78ab809cfe9
Результирующая строка будет вдвое длиннее, чем генерируемые вами случайные байты; каждый байт, закодированный в шестнадцатеричный формат, состоит из 2 символов. 20 байтов будут 40 шестнадцатеричными символами.
Используя 20 байтов, мы получаем
256^20
или 1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976 уникальных выходных значений. Это идентично возможному 160-битному (20-байтовому) выходу SHA1.Зная это, для нас не имеет значения
shasum
наши случайные байты. Это как дважды бросить кубик, но принять только второй бросок; несмотря ни на что, в каждом броске у вас есть 6 возможных результатов, поэтому первого броска достаточно.Почему так лучше?
Чтобы понять, почему это лучше, мы сначала должны понять, как работают функции хеширования. Функции хеширования (включая SHA1) всегда будут генерировать один и тот же вывод, если задан один и тот же ввод.
Допустим, мы хотим сгенерировать идентификаторы, но наш случайный ввод генерируется подбрасыванием монеты. У нас есть
"heads"
или"tails"
Если
"heads"
снова появится, выход SHA1 будет таким же, как и в первый раз.Итак, бросок монеты - не лучший генератор случайных идентификаторов, потому что у нас есть только 2 возможных выхода.
Если мы используем стандартный 6-сторонний кристалл, у нас есть 6 возможных входов. Угадайте, сколько возможных выходов SHA1? 6!
Это легко обманываться, думая только потому , что выход наших функций выглядят очень случайно, что она является очень случайной.
Мы оба согласны с тем, что подбрасывание монеты или шестигранная игральная кость будет плохим генератором случайных идентификаторов, потому что наши возможные результаты SHA1 (значение, которое мы используем для идентификатора) очень малы. Но что, если мы воспользуемся чем-то, у которого гораздо больше выходов? Как временная метка с миллисекундами? Или JavaScript
Math.random
? Или даже комбинация этих двух ?!Давайте посчитаем, сколько уникальных идентификаторов мы получим ...
Уникальность отметки времени в миллисекундах
При использовании
(new Date()).valueOf().toString()
вы получаете 13-значное число (например,1375369309741
). Однако, поскольку это число, обновляемое последовательно (один раз в миллисекунду), выходные данные почти всегда одинаковы. Давайте взглянемfor (var i=0; i<10; i++) { console.log((new Date()).valueOf().toString()); } console.log("OMG so not random"); // 1375369431838 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431840 // 1375369431840 // OMG so not random
Честно говоря, в целях сравнения, в данную минуту (большое время выполнения операции) у вас будут
60*1000
или60000
уникальные посетители.Уникальность
Math.random
Теперь при использовании
Math.random
из-за того, как JavaScript представляет 64-битные числа с плавающей запятой, вы получите число длиной от 13 до 24 символов. Более длинный результат означает больше цифр, что означает больше энтропии. Во-первых, нам нужно выяснить, какая длина является наиболее вероятной.Приведенный ниже сценарий определит, какая длина наиболее вероятна. Мы делаем это, генерируя 1 миллион случайных чисел и увеличивая счетчик в зависимости
.length
от каждого числа.// get distribution var counts = [], rand, len; for (var i=0; i<1000000; i++) { rand = Math.random(); len = String(rand).length; if (counts[len] === undefined) counts[len] = 0; counts[len] += 1; } // calculate % frequency var freq = counts.map(function(n) { return n/1000000 *100 });
Разделив каждый счетчик на 1 миллион, мы получим вероятность длины возвращаемого числа
Math.random
.Итак, даже если это не совсем так, давайте проявим щедрость и скажем, что вы получаете случайный вывод длиной 19 символов;
0.1234567890123456789
. Первыми символами всегда будут0
и.
, поэтому на самом деле мы получаем только 17 случайных символов. Это оставляет нам10^17
+1
(если возможно0
; см. Примечания ниже) или 100000000000000001 уникальный посетитель.Итак, сколько случайных входов мы можем сгенерировать?
Хорошо, мы подсчитали количество результатов для миллисекундной отметки времени и
Math.random
Это один кубик с 6 000 000 000 000 000 060 000 граней. Или, чтобы сделать это число более усваиваемым человеком, это примерно такое же число, как
Звучит неплохо, правда? Что ж, давайте выясним ...
SHA1 выдает 20-байтовое значение с возможными 256 ^ 20 результатами. Так что мы действительно не используем SHA1 в полной мере. Ну сколько мы используем?
Метка времени в миллисекундах и Math.random использует только 4,11e-27 процентов 160-битного потенциала SHA1!
Святые кошки, мужик! Посмотрите на все эти нули. Так насколько лучше
crypto.randomBytes(20)
? В 243,583,606,221,817,150,598,111,409 раз лучше.Примечания относительно
+1
частоты и частоты обнуленияЕсли вам интересно
+1
, возможно,Math.random
вернуть,0
что означает, что есть еще 1 возможный уникальный результат, который мы должны учитывать.Основываясь на обсуждении, которое произошло ниже, мне было любопытно, с какой частотой
0
будет появляться a . Вот небольшой сценарийrandom_zero.js
, который я сделал, чтобы получить данные#!/usr/bin/env node var count = 0; while (Math.random() !== 0) count++; console.log(count);
Затем я запустил его в 4 потока (у меня 4-ядерный процессор), добавив вывод в файл
$ yes | xargs -n 1 -P 4 node random_zero.js >> zeroes.txt
Получается, что получить a
0
не так уж и сложно. После записи 100 значений среднее значение былоКруто! Больше исследования необходимо будет знать , если это число на одном уровне с равномерным распределением v8 по
Math.random
реализацииисточник
Date
выращивать хорошие семена.Math.random
что когда-нибудь выпустил бы0.
crypto.randomBytes
определенноСделайте это и в браузере!
Вы можете сделать это на стороне клиента в современных браузерах, если хотите
// str byteToHex(uint8 byte) // converts a single byte to a hex string function byteToHex(byte) { return ('0' + byte.toString(16)).slice(-2); } // str generateId(int len); // len - must be an even number (default: 40) function generateId(len = 40) { var arr = new Uint8Array(len / 2); window.crypto.getRandomValues(arr); return Array.from(arr, byteToHex).join(""); } console.log(generateId()) // "1e6ef8d5c851a3b5c5ad78f96dd086e4a77da800" console.log(generateId(20)) // "d2180620d8f781178840"
Требования к браузеру
источник
Number.toString(radix)
не всегда гарантирует двузначное значение (например:(5).toString(16)
= "5", а не "05"). Это не имеет значения, если только вы не зависите от того, что ваш окончательный результат будет содержать ровноlen
символы. В этом случае вы можете использоватьreturn ('0'+n.toString(16)).slice(-2);
внутри своей карты функцию.id
атрибута, убедитесь, что идентификатор начинается с буквы: [A-Za-z].Использование
crypto
- хороший подход, потому что это собственный и стабильный модуль, но есть случаи, когда вы можете использовать его,bcrypt
если хотите создать действительно надежный и безопасный хеш. Я использую его для паролей, у него много методов хеширования, создания соли и сравнения паролей.const salt = bcrypt.genSaltSync(saltRounds); const hash = bcrypt.hashSync(myPlaintextPassword, salt);
const hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
Дополнительные примеры вы можете найти здесь: https://www.npmjs.com/package/bcrypt
источник