- ブログ
【パスワードつくるくん開発裏話】正規分布するランダム
2023.08.09 Wed
「逆ソクラテス/伊坂幸太郎」は最高だと思います。エンジニア1年生の つなまよ です。
今回は、ホエールテック入社4か月目に、つなまよひとりで開発を任された【パスワードつくるくん】の開発において、つなまよがつまづいた、パスワードにおける【ランダム】とは何なのかについてまとめたいと思います。
パスワードとランダムな文字列の違い
つなまよが【パスワードつくるくん】を開発するにあたって、解釈に時間がかかったポイントは「パスワード」と「ランダムな文字列」の違いでした。
「ランダムな文字列」のランダム性は、生成過程の無作為性に焦点がある一方、「パスワード」のランダム性は生成過程ではなく、生成されたものが予測困難か、に焦点があるのではないでしょうか。
この違いの例として、数字とアルファベット(大文字)を条件にランダムな文字列を作った場合、
・数字は、0-9の10種類
・アルファベット小文字は、a-zの26種類
なので、アルファベットが選択される確率が高いと言えます。
ですが、「パスワード」として格好良いかを考えるのであれば、数字とアルファベットがバランスよく含まれていることが大事だと思うのです。
ランダムの結果として偏った文字列が出力されてしまうと、「パスワード」としてはいささか格好悪いように思うわけです。
正規分布するランダムな値を使う
そして、たどり着いた考え方が、正規分布するランダムです。
生成するパスワードの文字数が16文字で、使用する文字種類が数字、英語大文字、英語小文字、記号の4種類の場合、それぞれが4文字ずつ含まれているのが均等です。
ですが、均等 = 規則性であることから、生成されたパスワードを推測する材料を与えてしまうことになりかねないため、適度に不規則であることが重要だと考えました。
つなまよにとっての理想の状態は「大体4文字だけどたまに2文字とか6文字含まれる」ことです。
ちなみに、出力結果が次のように分布する分布図を正規分布といいます。

対して、出力結果が1-16の間で均一の確率で出力される場合、次のようになります。

格好良いパスワードが、複数の文字種を(ある程度)バランスよく含んだ文字列だと想定し、【パスワードつくるくん】では以下のような【ランダム】を使用することにしました。
・文字種ごとの文字数は正規分布するランダム
・文字種からどの文字が選ばれるかは一様分布するランダム
というように使い分ける方法を選択しました。
JavaScriptで正規分布するランダムな値を取得する
それでは、実際に、JavaScriptで正規分布する値を取得するメソッドを書いてみます。
調べたところ、Box-Muller法を使ってなんやかんやすることで、正規分布するランダムな値が取れるようです。
今回はそこに、平均値、標準偏差(平均値からの離れやすさ)、最低値、最高値を引数に加えることで、カスタムのしやすいランダムな値を取得できるようにしました。
// 引数を中心とした正規分布(average:平均値,standardDeviation:標準偏差)
function rnorm(average:number, standardDeviation:number, min:number, max:number) {
let value:number = 0;
do {
value = average + standardDeviation * Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random());
} while (value < min || value > max);
return value;
}このコードでは、
0を中心として正規分布するランダムな値 × 標準偏差 + 平均値 = value
valueが最低値から最高値の間に収まらなければやり直し
という流れにしてみました。
このメソッドを使用して文字数を計算することで、「大体4文字だけどたまに2文字とか6文字」を実現することができました。
今回はここまで。つなまよでした。
【パスワードつくるくん】はこちらで公開されていますので、ぜひお試しください。
それから、【パスワードつくるくん】の紹介記事はこちらです。(ちなみに、この記事で紹介されている画像は、ランダムに向き合う前のパスワード生成処理のキャプチャで、格好悪いパスワードになっています・・・。差し替え希望!)