JavaScriptで乱数を利用したい場合はMath.random()
を使うことがほとんどだと思いますが、内部のロジック的に暗号などセキュアな用途には向いていないとされています。Node.jsでは標準モジュールであるcryptoを利用することでこの問題を解決することができます。
サンプルコード
結論から言うと具体的なコードは以下になります。getSecureRandom()
関数を実行する度にランダムな整数を取得できます。
const Crypto = require("crypto"); function getSecureRandom(){ const buff = Crypto.randomBytes(8); // バイナリで8byteのランダムな値を生成 const hex = buff.toString("hex"); // 16進数の文字列に変換 return ( parseInt(hex,16) ); // integerに変換して返却 }
パフォーマンス
気になるのは実行時の速度ですね。もちろんcrypto
モジュールのほうが重たいのは想像できるのですがどの程度の差が生まれるのか計測してみましょう。今回は以下の2種類のコードを用意しました。
Math.random()版
Math.random()
を100万回実行するシンプルなコードです。
for(let i=0; i<1000000; i++){ Math.random(); }
crypto版
こちらも同様に100万回実行します。
const Crypto = require("crypto"); for(let i=0; i<1000000; i++){ const buff = Crypto.randomBytes(8); const hex = buff.toString("hex"); parseInt(hex,16); }
実行結果
前者がMath.random()
、後者がcrypto
モジュールです。
$ time node test1.js node test1.js 0.09s user 0.03s system 91% cpu 0.132 total $ time node test2.js node test2.js 4.04s user 0.17s system 105% cpu 3.997 total
最終的な実行スピードだけ見るとなんと約30倍の差があることがわかりました(3.997/0.132)。ちなみにCrypto.randomBytes()
で生成するバイト数を2〜4に減らしても結果はそれほど大きく変わりませんでした。
ゲームやアニメーションのようなパフォーマンスを求められる場面では普通にMath.random()
を使ったほうが良さそうですね。まぁそもそも使わないと思いますがw
なお今回実行したNodeのバージョンは以下になります。
$ node --version v12.6.0
Webブラウザの場合
GoogleChromeなどWebブラウザで実行する場合はwindow.crypto.getRandomValues()
で同様にセキュアな乱数を生成可能です。具体的なコードはMDNを参照ください。
developer.mozilla.org
執筆時点(2019-07-14)だと「Can I use」によると96.3%のブラウザが利用可能なようです。