Math.randomは安全ではないのでcrypto.getRandomValuesを使うべし

Hacking the JavaScript Lotteryが注目される

はてなブックマークを見ていたら、Hacking the JavaScript Lotteryの記事が人気エントリーの一覧にありました。

この記事は2016年5月17日に投稿された古い記事なのですが、知らない方も多いと思うので、簡単に解説します。

Math.randomは安全ではない

Hacking the JavaScript Lotteryの記事はJavaScriptの乱数生成器(Math.random())をハッキングする方法について解説しています。

特に、XorShift128+ アルゴリズムを使用した乱数生成の仕組みを深く掘り下げ、ブラウザ内で生成された乱数からその内部状態を予測する方法を解説しています。

Z3というSMTソルバーを使い、生成された乱数からブラウザの乱数生成器の状態を再現し、次の乱数を予測することが可能であると説明しています。

crypto.getRandomValuesとは

crypto.getRandomValuesは暗号学的に安全なランダム値を生成するためのメソッドです。

指定された型(例えばUint32Arrayなど)の配列にランダムな値を埋め込み、これにより予測不可能な乱数を得ることができます。

これを使うことで、Math.random()よりもセキュリティ的に安全な乱数生成が可能です。

以下のように関数化しておけば、Math.random()の代わりにrandom()を使用することが容易になります。

JavaScript
console.log(Math.random())
// 例: 0.2695238037246708

function random() {
  const array = new Uint32Array(1)
  crypto.getRandomValues(array)
  return array[0] / (0xFFFFFFFF + 1)
}
console.log(random())
// 例: 0.445003256900236

Math.random()を仮パスワードやゲームの演算などで使用しているケースがありますが、Math.random()は次の乱数を予測することが可能な関数です。

次の乱数が予測されては困る場合は、必ずcrypto.getRandomValuesの乱数生成を使用してください。