नोड में आईडी के रूप में उपयोग करने के लिए यादृच्छिक SHA1 हैश कैसे उत्पन्न करें?


137

मैं इस लाइन का उपयोग कर रहा हूँ।

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

समस्या यह है कि यह हर बार एक ही आईडी लौटा रहा है।

क्या यह संभव है कि यह हर बार एक यादृच्छिक आईडी उत्पन्न करे ताकि मैं इसे डेटाबेस दस्तावेज़ आईडी के रूप में उपयोग कर सकूं?


2
Sha1 का उपयोग न करें। इसे अब सुरक्षित (टक्कर-प्रतिरोधी) नहीं माना जाता है। यही कारण है कि नोमिक का जवाब बेहतर है।
नील्स एबिल्डगार्ड

जवाबों:


60

यहाँ एक नज़र है: मैं एक HMAC-SHA1 हैश बनाने के लिए नोड.जेएस क्रिप्टो का उपयोग कैसे करूँ? मैं वर्तमान टाइमस्टैम्प का एक हैश बनाऊंगा + हैश विशिष्टता सुनिश्चित करने के लिए एक यादृच्छिक संख्या:

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

44
ज्यादा बेहतर दृष्टिकोण के लिए, नीचे @ naomik का उत्तर देखें।
गेबी पुरकारू

2
यह भी एक महान जवाब था गैबी, और बस थोड़ा सा तेज, लगभग 15%। दोनों महान काम! मैं वास्तव में नमक में एक तारीख () देखना पसंद करता हूं, यह एक डेवलपर को आसान विश्वास दिलाता है कि यह सभी लेकिन सबसे पागल समानांतर कंप्यूटिंग स्थितियों में अद्वितीय मूल्य होगा। मुझे पता है कि इसकी मूर्खतापूर्ण और यादृच्छिकताएं (20) अद्वितीय होने जा रही हैं, लेकिन इसका सिर्फ एक विश्वास हमारे पास हो सकता है क्योंकि हम किसी अन्य पुस्तकालय के यादृच्छिक पीढ़ी के आंतरिक लोगों से परिचित नहीं हो सकते हैं।
दिमित्री R117

636

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 अनूठे आउटपुट मान हैं। यह वह जगह है समान SHA1 के 160-बिट (20-बाइट) संभव आउटपुट से।

यह जानना, यह वास्तव में shasumहमारे यादृच्छिक बाइट्स के लिए सार्थक नहीं है । यह दो बार मरने की तरह है लेकिन केवल दूसरे रोल को स्वीकार करना; कोई बात नहीं, आपके पास प्रत्येक रोल के 6 संभावित परिणाम हैं, इसलिए पहला रोल पर्याप्त है।


यह बेहतर क्यों है?

यह समझने के लिए कि यह बेहतर क्यों है, हमें पहले यह समझना होगा कि हैशिंग कार्य कैसे करते हैं। यदि समान इनपुट दिया जाता है, तो हाशिंग फ़ंक्शंस (SHA1 सहित) हमेशा वही आउटपुट उत्पन्न करेगा।

मान लें कि हम आईडी जनरेट करना चाहते हैं, लेकिन हमारा रैंडम इनपुट एक सिक्का टॉस द्वारा उत्पन्न होता है। हमारे पास "heads"या है"tails"

% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f  -

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

यदि "heads"फिर से आता है, तो SHA1 आउटपुट वही होगा जो पहली बार था

% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f  -

ठीक है, इसलिए एक सिक्का टॉस एक महान यादृच्छिक आईडी जनरेटर नहीं है क्योंकि हमारे पास केवल 2 संभावित आउटपुट हैं।

अगर हम मानक 6-पक्षीय डाई का उपयोग करते हैं, तो हमारे पास 6 संभावित इनपुट हैं। लगता है कि कितने संभव SHA1 आउटपुट? 6!

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

यह सिर्फ इसलिए है क्योंकि हमारे फ़ंक्शन के परिणाम सोच कर खुद को भुलाना आसान है दिखता है , बहुत यादृच्छिक है कि यह है बहुत यादृच्छिक।

हम दोनों सहमत हैं कि एक सिक्का टॉस या 6-पक्षीय मृत्यु एक बुरा यादृच्छिक आईडी जनरेटर बना देगा, क्योंकि हमारे संभावित SHA1 परिणाम (आईडी के लिए हम जो मूल्य का उपयोग करते हैं) बहुत कम हैं। लेकिन क्या होगा अगर हम किसी ऐसी चीज का उपयोग करें जिसमें बहुत अधिक आउटपुट हैं? मिलीसेकंड के साथ टाइमस्टैम्प की तरह? या जावास्क्रिप्ट है 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जावास्क्रिप्ट 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

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 uniques के साथ छोड़ देता है ।


तो हम कितने यादृच्छिक इनपुट उत्पन्न कर सकते हैं?

ठीक है, हमने एक मिलीसेकंड टाइमस्टैम्प और के लिए परिणामों की संख्या की गणना की 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 ^ 20 परिणामों के साथ, 20-बाइट मान का उत्पादन करता है। तो हम वास्तव में पूरी क्षमता तक SHA1 का उपयोग नहीं कर रहे हैं। वैसे हम कितना उपयोग कर रहे हैं?

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

एक मिलीसेकंड टाइमस्टैम्प और Math.random केवल 4.11e-27 प्रतिशत SHA1 की 160-बिट क्षमता का उपयोग करता है!

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

पवित्र बिल्लियों, आदमी! उन सभी शून्य को देखो। तो कितना बेहतर है crypto.randomBytes(20)? 243,583,606,221,817,150,598,111,409 गुना बेहतर।


+1और शून्य की आवृत्ति के बारे में नोट्स

आप के बारे में सोच रहे हैं +1, यह संभव है के लिए Math.randomएक वापस जाने के लिए 0जो वहाँ 1 और अधिक संभव अद्वितीय परिणाम हम के लिए खाते में राशि का मतलब है।

नीचे हुई चर्चा के आधार पर, मैं इस बात के बारे में उत्सुक था कि आवृत्ति 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

तो यह पता चला है कि एक 0मुश्किल नहीं है। 100 मान दर्ज किए जाने के बाद , औसत था

3,164,854,823 में 1 रैंडम 0 है

ठंडा! यह जानने के लिए अधिक शोध की आवश्यकता होगी कि क्या वह संख्या v8 के Math.randomकार्यान्वयन के समान वितरण के साथ चालू है


2
कृपया मेरा अपडेट देखें; यहां तक ​​कि एक मिलीसेकंड लाइटस्पेड जावास्क्रिप्ट भूमि में एक लंबा समय है! अधिक गंभीर नोट पर, संख्या के पहले 10 अंक हर सेकंड समान रहते हैं; यह वही है जो Dateअच्छे बीजों के उत्पादन में भयानक है।
आपको

1
सही बात। हालाँकि मैं वास्तव में केवल उन लोगों को शामिल करता था जो दूसरे उत्तर के लिए सबसे अधिक योगदान देते थे कि 20 यादृच्छिक बाइट्स अभी भी केवल एन्ट्रापी के संदर्भ में हावी हैं। मुझे नहीं लगता Math.randomकि कभी भी एक उत्पादन होगा0.
धन्यवाद

8
स्वीकार किए गए उत्तर की तुलना में 14 गुना अधिक उत्थान ... लेकिन गिनती किसकी है? :)
zx81

2
@ मोका, पासा मरने का बहुवचन रूप है । मैं एकवचन रूप का उपयोग कर रहा हूं।
धन्यवाद

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)= "5", "05" नहीं)। यह तब तक मायने नहीं रखता जब तक कि आप अपने अंतिम आउटपुट पर निर्भर न हों कि वास्तव में lenवर्ण लंबे हों। इस मामले में आप return ('0'+n.toString(16)).slice(-2);अपने मानचित्र फ़ंक्शन के अंदर उपयोग कर सकते हैं ।
The Brawny Man

1
महान कोड, धन्यवाद। बस जोड़ना चाहता था: यदि आप इसे एक idविशेषता के मूल्य के लिए उपयोग करने जा रहे हैं , तो सुनिश्चित करें कि आईडी एक पत्र से शुरू होती है: [ए-ज़-ज़]।
गिज्जनबी

शानदार उत्तर (और टिप्पणियाँ) - वास्तव में सराहना की कि आपने उत्तर में ब्राउज़र की आवश्यकताओं को भी शामिल किया है!
केवलर

ब्राउज़र आवश्यकताएँ गलत हैं। IE11 में Array.from () समर्थित नहीं है
उपसर्ग

1
यह उत्तर के समय एक विकि से लिया गया था। आप चाहें तो इस उत्तर को संपादित कर सकते हैं, लेकिन वास्तव में IE की परवाह कौन करता है? यदि आप इसे समर्थन देने की कोशिश कर रहे हैं, तो आपको वैसे भी जावास्क्रिप्ट के आधे हिस्से को पॉलीफ़िल करना होगा ...
आपको धन्यवाद
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.