この行を使用して、node.jsのsha1IDを生成しています。
crypto.createHash('sha1').digest('hex');
問題は、毎回同じIDを返すことです。
毎回ランダムなIDを生成して、データベースドキュメントIDとして使用できるようにすることはできますか?
この行を使用して、node.jsのsha1IDを生成しています。
crypto.createHash('sha1').digest('hex');
問題は、毎回同じIDを返すことです。
毎回ランダムなIDを生成して、データベースドキュメントIDとして使用できるようにすることはできますか?
crypto.randomBytesを使用することをお勧めします。それはそうではありませんsha1
が、idの目的のために、それはより速く、そしてちょうど「ランダム」です。
var id = crypto.randomBytes(20).toString('hex');
//=> f26d60305dae929ef8640a75e70dd78ab809cfe9
結果の文字列は、生成するランダムバイトの2倍の長さになります。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
これを知っていると、ランダムなバイトにとってはあまり意味がありません。これは、サイコロを2回振るようなものですが、2回目の振るだけを受け入れます。いずれにせよ、各ロールで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
コイントスまたは6面ダイスは、SHA1の結果(IDに使用する値)が非常に少ないため、ランダムIDジェネレーターが不適切になることに同意します。しかし、より多くの出力を持つものを使用するとどうなるでしょうか。ミリ秒のタイムスタンプのように?またはJavaScriptのMath.random
?またはそれらの2つの組み合わせでさえ?!
取得する一意のIDの数を計算してみましょう...
ミリ秒単位のタイムスタンプの一意性
を使用する(new Date()).valueOf().toString()
と、13文字の数字が表示されます(例:)1375369309741
。ただし、これは順次更新される数値(1ミリ秒に1回)であるため、出力はほとんど常に同じです。見てみましょう
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文字の長さの数値が得られます。結果が長いほど、桁数が多くなり、エントロピーが多くなります。まず、最も可能性の高い長さを見つける必要があります。
以下のスクリプトは、どの長さが最も可能性が高いかを決定します。これを行うには、100万個の乱数を生成し.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 });
各カウンターを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
and.
であるため、実際には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
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つあることを意味します。
以下で起こった議論に基づいて、私はaが現れる頻度について興味があり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
したがって、a0
を取得するのはそれほど難しくないことがわかります。100の値が記録された後、平均は
3,164,854,823ランダムの1つは0です
涼しい!Math.random
その数がv8の実装の一様分布と同等であるかどうかを知るには、さらに調査が必要です。
ここを見てください: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');
編集:これは私の前の答えの流れに実際には適合しませんでした。ブラウザでこれを実行しようとしている可能性のある人々のための2番目の答えとしてここに残しておきます。
必要に応じて、このクライアント側を最新のブラウザで実行できます
// 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
ネイティブで安定したモジュールであるため、使用crypto
することは良いアプローチですがbcrypt
、本当に強力で安全なハッシュを作成したい場合に使用できる場合があります。私はそれをパスワードに使用します。ハッシュ、ソルトの作成、パスワードの比較のための多くのテクニックがあります。
テクニック1(別々の関数呼び出しでソルトとハッシュを生成する)
const salt = bcrypt.genSaltSync(saltRounds);
const hash = bcrypt.hashSync(myPlaintextPassword, salt);
テクニック2(ソルトとハッシュの自動生成):
const hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
その他の例については、 https://www.npmjs.com/package/bcryptで確認できます。
一意の識別子を取得する場合は、UUID(ユニバーサル一意識別子)/ GUID(グローバル一意識別子)を使用する必要があります。
ハッシュは、任意のサイズの入力に対して、決定論的で一意であり、固定長であると想定されています。したがって、ハッシュ関数を何度実行しても、同じ入力を使用すると出力は同じになります。
UUIDは一意でランダムに生成されます!'uuid'というパッケージがあります。npm経由でインストールできます。
npm install uuid
&コードでモジュールをインポートします
const {v4:uuidv4} = require('uuid');
//メソッドuuidv4または名前を付けたものをインポートしてログに記録するか、保存または割り当てるときに呼び出します。このメソッドは、文字列の形式でUUIDを返します。
console.log(uuidv4()); //出力例: '59594fc8-6a35-4f50-a966-4d735d8402ea'
これがnpmリンクです(必要な場合): https ://www.npmjs.com/package/uuid