そこで、コンピュータがどうやって「ランダムらしきもの」を作ってきたのか、その歴史を振り返り、JavaScriptのMath.randomとwindow.crypto.getRandomValuesという2つの方法の違いを説明する。最後に、パスワード生成ツール「makepassword.html」のプログラム仕様書を示す。
目次
パスワードをランダムに生成する仕組み
パスワード生成ツールの仕組みは、実はそれほど難しくない。あらかじめ「使ってよい文字」を1つの袋に入れておき、その袋から手を入れて文字を1つずつ取り出す。これを、指定した文字数の分だけ繰り返せばパスワードが完成する。袋の中身は、下表の4種類のグループに分けて用意する。
| 文字種 | 文字の例 |
|---|---|
| 数字 | 0123456789 |
| 英大文字 | ABCDEFGHIJKLMNOPQRSTUVWXYZ |
| 英小文字 | abcdefghijklmnopqrstuvwxyz |
| 記号 | ()%$#+-*(ユーザーが変更できる) |
ユーザーがチェックを入れた文字種だけを袋の中身として合体させ、そこから1文字ずつランダムに取り出す。「同じ文字が2つ以上連続してよいか」というチェックボックスがONのときは、直前に取り出した文字と同じ文字が続けて出てもそのまま採用する。OFFのときは、直前と同じ文字が出た場合はもう一度取り出しをやり直す。この「ランダムに取り出す」という部分こそが、次節で説明する乱数の出番である。
コンピュータがランダムを苦手とする理由
サイコロを振ったり、コインを投げたりすると、次に何が出るかは誰にも予測できない。これが本来の意味でのランダム(randomness)である。ところが、コンピュータは与えられた手順(プログラム)を1つずつ正確に実行する機械であり、同じ入力を与えれば必ず同じ結果を返す。つまり、コンピュータの動作は本質的に決定論的(deterministic)であり、サイコロのような偶然性を内部に持たない。
そこでコンピュータは、複雑な計算式を使って「人間の目には偶然に見える数列」を作り出す。これを擬似乱数(pseudo-random number)と呼ぶ。擬似乱数は、最初に与える種(シード、seed)という値が決まれば、その後に出てくる数列もすべて決まってしまう。種が同じであれば、何度実行しても同じ数列が再現される。JavaScriptのMath.randomも、この擬似乱数の一種である。
コンピュータによる乱数の歴史
コンピュータが登場する以前から、統計学や暗号の分野では「質の良い乱数」を大量に得る方法が研究されていた。コンピュータによる乱数の歴史をたどると、下表のような流れになる。
| 年代 | 出来事 |
|---|---|
| 1946年ごろ | ジョン・フォン・ノイマンが「中央二乗法(middle-square method)」を考案。数値を2乗して真ん中の桁を取り出すことを繰り返す方式。パンチカードから乱数を読み込むより100倍以上速かったが、やがて同じ数列を繰り返してしまう欠点があった。 |
| 1955年 | 米国のRAND研究所が、電子的な雑音(ノイズ)を発生させる装置を使って100万個の乱数を集め、書籍「A Million Random Digits with 100,000 Normal Deviates」として発表。 |
| 1957年 | 英国で、旧ブレッチリー・パークの技術者トミー・フラワーズらが専用装置「ERNIE(Electronic Random Number Indicator Equipment)」を開発。電子回路の雑音を利用して、プレミアム・ボンド(英国の抽選付き国債)の当選番号を選ぶために使われた。 |
| 1997年 | 松本眞と西村拓士が「メルセンヌ・ツイスタ(Mersenne Twister)」を発表。長い周期と高速な計算を両立し、多くのプログラミング言語の標準的な擬似乱数生成器として採用された。 |
このように、コンピュータの乱数は「本物の偶然(ハードウェアの雑音など)」と「計算式による擬似乱数」の2つの流れを行き来しながら発展してきた。現在のブラウザが備えるMath.randomとwindow.crypto.getRandomValuesも、この2つの流れをそれぞれ受け継いでいる。
Math.randomとwindow.crypto.getRandomValuesの違い
JavaScriptには、乱数を得る方法が主に2つ用意されている。Math.randomとwindow.crypto.getRandomValuesである。両者は「0以上1未満の数、あるいはランダムな数値を返す」という点では似ているが、内部の仕組みと用途がまったく異なる。
| 比較項目 | Math.random | window.crypto.getRandomValues |
|---|---|---|
| 生成方法 | 擬似乱数(PRNG) | 暗号論的擬似乱数(CSPRNG) |
| 数値の元 | 計算式(アルゴリズム)だけで生成 | OSが集めた雑音(エントロピー)を利用 |
| 予測されやすさ | 過去の出力から予測できる可能性がある | 予測は極めて困難 |
| 実行速度 | 速い | Math.randomより遅い |
| 向いている用途 | ゲームの演出、抽選の見た目など | パスワード、暗号鍵、トークンなど安全性が必要な用途 |
Math.randomは内部で計算式だけを使って数値を作るため、種の値さえ推測されれば、その後に出てくる数値の並びを予測できてしまう可能性がある。一方、window.crypto.getRandomValuesは、OS(Windows、macOS、Android、iOSなど)が集めている雑音を元にした暗号論的擬似乱数生成器(CSPRNG)を利用する。マウスの動きや電気的な雑音など、予測しにくい情報を種として使うため、Math.randomより予測が難しい。

パスワードや暗号鍵のように、他人に予測されると困る値を作るときは、window.crypto.getRandomValuesを使うべきである。ゲームの当たり演出やアニメーションの動きなど、予測されても実害がない用途であれば、速度に優れたMath.randomでもかまわない。パスワード生成ツールでは安全性が重要なので、window.crypto.getRandomValuesを使うようCodexに指示する。
パスワードや暗号鍵のように、他人に予測されると困る値を作るときは、window.crypto.getRandomValuesを使うべきである。ゲームの当たり演出やアニメーションの動きなど、予測されても実害がない用途であれば、速度に優れたMath.randomでもかまわない。パスワード生成ツールでは安全性が重要なので、window.crypto.getRandomValuesを使うようCodexに指示する。
パスワード生成ツールのプログラム仕様書
以上を踏まえて、パスワード生成ツール「makepassword.html」のプログラム仕様書を示す。これがCodexに渡すプロンプトそのものになる。
プログラム仕様書(プロンプト)
# 目標 パスワードを生成する。
# プログラム・ファイル名 makepassword.html
# プロジェクト・フォルダ作成 - プログラム・ファイル名の拡張子を除いた主ファイル名と同じ名前のサブフォルダを作成し、以降の作業はサブフォルダで行う。 - すでにサブフォルダがあれば、そのサブフォルダに移動して以降の作業を進める。
## 入力 - ユーザーはパスワード長(文字数)を入力する。デフォルト値は10文字。 - ユーザーは同じ文字が2つ以上連続していいかどうかのチェックボックスを選ぶ。デフォルトはOFF。 - ユーザーはパスワード文字種(数字、英大文字、英小文字、記号)を選択する。デフォルト値はすべてにチェックが入っている。 - 記号はテキストボックスにしておきユーザーが都度変更できるようにする。デフォルト値は ()%$#+-*
## 処理 - パスワードは可能な限りランダムな組み合わせを選ぶ。
## 通信 なし。
## 出力 - プログラム上部にタイトル「パスワード生成機」、バージョン番号、製作者「(c)pahoo.org Powered by Codex」と記載する。 - ユーザーが「生成」ボタンをクリックすると、テキストボックスにパスワードを表示する。 - パスワードは、入力で指定した長さ、文字種、連続の有無にしたがって生成する。 - ユーザーが「コピー」ボタンをクリックすると、パスワードをクリップボードにコピーする。
## 例外・エラー処理 - 無限ループに陥ったり、システム・エラーが出たときは、画面にエラー情報を表示して終了すること。
## 記録 なし。
# テスト観点・合格条件 - Codexが、パスワード長、パスワード文字種を変えて10回実行する。 - 前提条件、制約条件が守られていること。
# 前提条件 - タイトルは「パスワード生成機」、バージョン番号、著作権者を表示する。 - 仕様で分からないことがあれば、ユーザーに質問すること。 - JavaScriptを使った1本のプログラム・ファイルにすること。 - クライアントPCのブラウザ(OSやブラウザの種類は問わない)で動作すること。 - スマホでも利用できること。 - httpサーバやNode.jsなどサーバ技術は使わず、ブラウザの機能で完結すること。 - コーディングは「Airbnb JavaScript Style Guide」にのっとること。 - プログラムファイルにコメントとして次の情報を記載すること。 - プログラムの名称 - バージョン - 目的 - 動作環境 - 著作権表示および使用条件 - インストール方法 - お問い合わせ
# 制約条件 - インターネットとのデータ送受信は行わないこと。 - 外部ライブラリを使用する場合は、下記のサイトに限定すること https://cdn.jsdelivr.net/ https://cdnjs.cloudflare.com/ https://ajax.googleapis.com/ https://code.jquery.com/ https://ajax.aspnetcdn.com/ - プログラムがMIT Licenseに違反していないこと。
「## 入力」に文字種・パスワード長・連続可否を、「## 出力」に画面表示とコピー機能を、「# 前提条件」「# 制約条件」に安全性と動作環境を書き込んである。この仕様書をCodexに渡すと、文字種の組み合わせや連続文字の除外処理まで含んだパスワード生成ツールが生成される。
完成したパスワード生成ツール
参考サイト
- C++ でパスワード生成機を作る:ぱふぅ家のホームページ
- PHPでパスワードを生成:ぱふぅ家のホームページ
- Math.random():MDN Web Docs
- Crypto: getRandomValues() メソッド:MDN Web Docs
- 擬似乱数:Wikipedia
- A Million Random Digits with 100,000 Normal Deviates:Wikipedia
(この項おわり)

ただし「ランダム」という言葉は、実はコンピュータにとって単純ではない。コンピュータは決まった手順どおりに動く機械なので、本来サイコロのような偶然性を持たない。