JavaScript
Node.js
セキュリティ
乱数

Node.jsでセキュアな乱数を生成したい!(0.0 ~ 1.0)


やりたいこと

0.0~1.0の間で小数の乱数をセキュアに生成したいです

以下のMDNによるとMath.random()はセキュアじゃないです。


Math.random() の提供する乱数は、暗号に使用可能な安全性を備えていません。セキュリティに関連する目的では使用しないで下さい。代わりにWeb Crypto API(より正確にはwindow.crypto.getRandomValues()メソッド)を使用して下さい。


なので、Math.random()のセキュア版を作りたいです。


セキュアな乱数生成

以下のsecureRandom()がセキュアなMath.random()のつもりです。

// cryptoをインポート

const crypto = require('crypto');

// crypto.randomBytes()で生成するときのバイト数
const nBytes = 4;
// nBytesの整数の最大値
const maxValue = 4294967295

// [0.0, 1.0]区間でセキュアな乱数を生成する
function secureRandom() {
// nBytesバイトのランダムなバッファを生成する
const randomBytes = crypto.randomBytes(nBytes);
// ランダムなバッファを整数値に変換する
const r = randomBytes.readUIntBE(0, nBytes);
// 最大値で割ることで、[0.0, 1.0]にする
return r / maxValue;
}


動かしてみる

以下で動作確認できます([run▶]を押してください)

https://repl.it/@nwtgck/secure-random-number


maxValueの算出について

maxValuenBytesを使えば生成できるはずなのです。JavaScriptでmaxValue == (1 << 8*nBytes) - 1なはずなので、ビット演算をしてみたのですが、うまく行かなかったので、ハードコーディングしています。長くなりますが、以下のように書くと、うまくnBytesからmaxValueを算出できました(なにかいい案があったら教えてもらえると嬉しいです)。

// (計算できるけど、ちょっと長い)

const maxValue = new Buffer(Array(nBytes).fill(0xff)).readUIntBE(0, nBytes);


Math.random()との違い

Math.random()は「[0, 1)(0以上、1未満)」らしいですが、secureRandom()は[0, 1]なので、そこが違うところだと思います。


参考