node.js에서 ID로 사용할 임의의 SHA1 해시를 생성하는 방법은 무엇입니까?


137

이 줄을 사용하여 node.js의 sha1 ID를 생성합니다.

crypto.createHash('sha1').digest('hex');

문제는 매번 동일한 ID를 반환한다는 것입니다.

매번 임의의 ID를 생성하여 데이터베이스 문서 ID로 사용할 수 있습니까?


2
sha1을 사용하지 마십시오. 더 이상 안전한 것으로 간주되지 않습니다 (충돌 방지). 이것이 나오미의 대답 이 더 나은 이유 입니다.
Niels Abildgaard

답변:


60

여기 보라 : 어떻게이 HMAC-SHA1 해시를 생성하는 암호화 Node.js를 사용합니까? 해시 고유성을 보장하기 위해 현재 타임 스탬프의 해시 + 난수를 만듭니다.

var current_date = (new Date()).valueOf().toString();
var random = Math.random().toString();
crypto.createHash('sha1').update(current_date + random).digest('hex');

44
더 나은 접근 방법은 아래 @naomik의 답변을 참조하십시오.
Gabi Purcaru

2
이것은 또한 훌륭한 대답 Gabi였으며 약 15 % 정도로 조금 더 빨랐습니다. 잘 했어! 실제로 소금에 Date ()를보고 싶습니다. 이것은 개발자가 가장 미친 병렬 컴퓨팅 상황을 제외하고는 고유 한 가치가 될 것이라는 확신을 쉽게줍니다. 나는 바보와 randomBytes (20)가 독특하다는 것을 알고 있지만, 다른 라이브러리의 무작위 생성의 내부에 익숙하지 않을 수 있기 때문에 확신 할 수 있습니다.
Dmitri R117

637

243,583,606,221,817,150,598,111,409 배 더 많은 엔트로피

crypto.randomBytes 사용하는 것이 좋습니다 . 그것은 sha1아니지만 ID 목적을 위해 더 빠르며 "무작위"입니다.

var id = crypto.randomBytes(20).toString('hex');
//=> f26d60305dae929ef8640a75e70dd78ab809cfe9

결과 문자열은 생성하는 임의 바이트의 두 배입니다. 16 진으로 인코딩 된 각 바이트는 2 자입니다. 20 바이트는 16 진수 40 자입니다.

20 바이트를 사용하여, 우리는이 256^20또는 1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976 고유의 출력 값. 이것은 SHA1의 160 비트 (20 바이트) 가능한 출력 과 동일 합니다.

이것을 알면 shasum무작위 바이트에 대해서는 의미가 없습니다 . 그것은 주사위를 두 번 굴리는 것과 같지만 두 번째 롤만 받아들입니다. 무엇이든, 각 롤마다 6 가지 가능한 결과가 있으므로 첫 번째 롤이면 충분합니다.


왜 이것이 더 낫습니까?

이것이 왜 더 좋은지 이해하려면 먼저 해싱 함수의 작동 방식을 이해해야합니다. 동일한 입력이 제공되면 해싱 함수 (SHA1 포함)는 항상 동일한 출력을 생성합니다.

ID를 생성하고 싶지만 무작위 입력은 동전 던지기에 의해 생성된다고 가정 해보십시오. 우리는 "heads"또는"tails"

% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f  -

% echo -n "tails" | shasum
71ac9eed6a76a285ae035fe84a251d56ae9485a4  -

경우 "heads"다시 제공의 SHA1 출력은 될 것 같은 가 처음으로

% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f  -

따라서 동전 던지기는 2 개의 가능한 출력 만 있기 때문에 큰 무작위 ID 생성기가 아닙니다.

표준 6면 다이를 사용하면 6 가지 입력이 가능합니다. 가능한 SHA1 출력 수는 몇 개입니까? 6!

input => (sha1) => output
1 => 356a192b7913b04c54574d18c28d46e6395428ab
2 => da4b9237bacccdf19c0760cab7aec4a8359010b0
3 => 77de68daecd823babbb58edb1c8e14d7106e83bb
4 => 1b6453892473a467d07372d45eb05abc2031647a
5 => ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
6 => c1dfd96eea8cc2b62785275bca38ac261256e278

그것은 단지 때문에 우리의 함수의 출력을 생각하여 자신을 속이는 쉽게 외모 는 것을 매우 무작위 이다 매우 무작위.

우리는 가능한 한 SHA1 결과 (ID에 사용하는 값)가 거의 없기 때문에 동전 던지기 또는 6면 주사위로 인해 무작위 임의 ID 생성기가 잘못 될 것이라는 데 동의합니다. 그러나 더 많은 출력을 가진 것을 사용한다면 어떨까요? 밀리 초가 포함 된 타임 스탬프처럼? 아니면 JavaScript Math.random? 아니면 그 둘 의 조합 ?!

우리가 얻을 수있는 고유 ID 수를 계산합시다 ...


밀리 초가 포함 된 타임 스탬프의 고유성

를 사용 (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.randomJavaScript를 사용하여 64 비트 부동 소수점 숫자를 나타내는 방식 때문에을 사용 하면 길이가 13에서 24 자 사이 인 숫자를 얻게됩니다. 더 긴 결과는 더 많은 숫자를 의미하며 더 많은 엔트로피를 의미합니다. 먼저 가장 가능성있는 길이를 찾아야합니다.

아래 스크립트는 가장 가능한 길이를 결정합니다. 우리는 백만 개의 난수를 생성하고 .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 });

각 카운터를 백만으로 나누면에서 반환되는 숫자의 길이를 얻을 수 있습니다 Math.random.

len   frequency(%)
------------------
13    0.0004  
14    0.0066  
15    0.0654  
16    0.6768  
17    6.6703  
18    61.133  <- highest probability
19    28.089  <- second highest probability
20    3.0287  
21    0.2989  
22    0.0262
23    0.0040
24    0.0004

따라서 완전히 사실이 아니더라도 관대하고 19 자 길이의 임의 출력을 얻습니다. 0.1234567890123456789. 첫 번째 문자는 항상 0.이므로 실제로는 17 개의 임의의 문자 만받습니다. 이로 인해 10^17 +1(가능하면 0아래 참고 사항 참조) 또는 100,000,000,000,000,001 고유 항목 이 남습니다 .


그렇다면 얼마나 많은 랜덤 입력을 생성 할 수 있습니까?

자, 우리는 밀리 초 타임 스탬프에 대한 결과 수를 계산했습니다. Math.random

      100,000,000,000,000,001 (Math.random)
*                      60,000 (timestamp)
-----------------------------
6,000,000,000,000,000,060,000

그것은 단일 6,000,000,000,000,000,060,000면 다이입니다. 또는, 더 인간적으로 소화이 숫자를 만들기 위해,이는 거의 같은 수의

input                                            outputs
------------------------------------------------------------------------------
( 1×) 6,000,000,000,000,000,060,000-sided die    6,000,000,000,000,000,060,000
(28×) 6-sided die                                6,140,942,214,464,815,497,21
(72×) 2-sided coins                              4,722,366,482,869,645,213,696

꽤 잘 들립니다. 글쎄, 알아 보자 ...

SHA1 은 20 바이트 값을 생성하며 256 ^ 20 개의 결과가 가능합니다. 따라서 우리는 잠재력을 최대한 발휘하기 위해 SHA1을 사용하지 않습니다. 우리는 얼마나 사용하고 있습니까?

node> 6000000000000000060000 / Math.pow(256,20) * 100

밀리 초 타임 스탬프와 Math.random은 SHA1의 160 비트 전위 중 4.11e-27 % 만 사용합니다!

generator               sha1 potential used
-----------------------------------------------------------------------------
crypto.randomBytes(20)  100%
Date() + Math.random()    0.00000000000000000000000000411%
6-sided die               0.000000000000000000000000000000000000000000000411%
A coin                    0.000000000000000000000000000000000000000000000137%

성스러운 고양이들! 그 0을 모두보십시오. 그래서 얼마나 낫 crypto.randomBytes(20)습니까? 243,583,606,221,817,150,598,111,409 배 더 좋습니다.


+10의 빈도 및 빈도에 대한 참고 사항

당신이에 대해 궁금해하는 경우 +1, 그것은 가능 Math.random돌아가 0우리가에 대한 계정이 하나 개 더 가능한 유일한 결과는 거기에 의미를.

아래에서 일어난 토론을 바탕으로, 나는 일어날 빈도에 대해 궁금 0했다. 다음은 약간의 스크립트입니다. 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 개의 값 이 기록 된 후 평균은

3,164,854,823의 1에서 랜덤 은 0입니다

멋있는! 이 숫자가 v8 Math.random구현 의 균일 한 분포와 일치하는지 확인하려면 더 많은 연구가 필요합니다.


2
내 업데이트를 참조하십시오; 가벼운 속도의 자바 스크립트 땅에서 밀리 초조차도 오랜 시간입니다! 더 심각한 점에서, 숫자의 처음 10 자리 숫자는 매초 동일하게 유지됩니다. 이것이 Date좋은 씨앗을 생산하는 데 끔찍한 일입니다.
감사합니다.

1
옳은. 비록 실제로 20 바이트의 바이트가 여전히 엔트로피 측면에서 우세하다는 것을 증명하기 위해 다른 답변에 가장 크게 기여하는 것들만 포함했지만. 나는 생각하지 않는다 Math.randomA는 지금까지 생산 것입니다0.
감사합니다

8
허용 된 답변보다 14 배 더 많은 지지율이 있지만 누가 계산합니까? :)
zx81 2016 년

2
@moka, 주사위 는 복수 형태의 주사위 입니다. 나는 단수형을 사용하고 있습니다.
감사합니다

2
crypto.randomBytes확실히 갈 길입니다 ^^
감사합니다

28

브라우저에서도하세요!

편집 : 이것은 이전 답변의 흐름에 실제로 맞지 않았습니다. 브라우저 에서이 작업을 수행하려는 사람들에게 두 번째 답변으로 여기에 남겨두고 있습니다.

원하는 경우 최신 브라우저에서이 클라이언트 측을 수행 할 수 있습니다.

// 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"

브라우저 요구 사항

Browser    Minimum Version
--------------------------
Chrome     11.0
Firefox    21.0
IE         11.0
Opera      15.0
Safari     5.1

3
Number.toString(radix)항상 2 자리 값을 보장하지는 않습니다 (예 : (5).toString(16)"05"가 아닌 "5"). 최종 출력이 정확히 len문자 길이 가 아닌 한 이것은 중요하지 않습니다 . 이 경우 return ('0'+n.toString(16)).slice(-2);맵 함수 내부에서 사용할 수 있습니다 .
Brawny Man

1
훌륭한 코드입니다. 감사합니다. id속성 값에 사용하려는 경우 ID가 문자로 시작하는지 확인하십시오. [A-Za-z].
GijsjanB

훌륭한 답변 (및 의견)-답변에 브라우저 요구 사항도 포함되어 있다는 점에 정말로 감사드립니다!
kevlarr

브라우저 요구 사항이 올바르지 않습니다. IE11 에서는 Array.from ()이 지원 되지 않습니다 .
접두사

1
이 답변 당시 위키에서 가져 왔습니다. 원하는 경우이 답변을 편집 할 수 있지만 누가 실제로 IE에 관심이 있습니까? 당신이 그것을 지원하려는 경우, 어쨌든 자바 스크립트의 절반을 polyfill해야합니다 ...
감사합니다
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.