ハッシュ・ID・乱数
テスト用IDやランダム文字列をまとめて用意したい
使うツール
画面の一覧を試すためにIDが欲しいとき、1、2、3でも足りることがあります。一方、招待リンクの秘密部分を同じ感覚で作るのは危険です。値に求めるものが、重ならないことなのか、推測しにくいことなのか、毎回同じテストを再現できることなのかで選ぶ方法が変わります。『ランダムだから全部同じ』とまとめず、三つの目的を分けて考えてみましょう。
UUIDを三つ作って、形式を読む
UUID生成・確認で生成件数を3、版をv4にします。結果は実行ごとに変わるので、この記事と同じ値になることは期待しません。典型的な表記は8-4-4-4-12桁の16進数で、ハイフンを含め36文字です。第三ブロックの先頭が4であることなど、版を表す決まったビットがあります。見た目が全部乱数でも、128bitすべてが自由な乱数というわけではありません。
550e8400-e29b-41d4-a716-446655440000生成した値を確認入力へ入れると、形式と版などを調べられます。ただし、形式が正しいことは、そのIDが実在する利用者やデータを指す証明にはなりません。このツールはデータベースへ問い合わせません。APIが受け付ける形式を確認するテストと、権限のあるデータだけを取得できるかのテストは別に用意します。識別子を長くするだけでアクセス制御を置き換えないようにします。
v7は並びやすいが、時刻の情報を含む
版をv7へ変えて生成すると、先頭側にUnix時刻のミリ秒を含むUUIDになります。確認機能では対応する時刻情報を読めます。これは日時で並べやすい構造ですが、処理が本当にその時刻に完了した証拠ではありません。端末時計や生成実装の影響を受けますし、IDを別の処理へ使い回せばイベント時刻とも一致しません。監査に必要な時刻は、IDからの推測だけに頼らず別の記録として扱います。
同じミリ秒の複数生成や、別端末で作ったIDの順番まで、ここで一律に保証するわけではありません。v4とv7は用途や保存先の特性で選びます。v7を秘密の招待コードとして使えば、時刻を含む構造も公開されます。『新しい版だからすべての用途で安全』ではなく、識別子として必要な並び方と、外へ見せてよい情報を分けて判断します。
秘密の値は、文字数より元のバイト数を見る
ランダムトークン生成で32バイト、16進数を選ぶと64文字になります。同じ32バイトをBase64urlにすると、パディングなしで43文字です。後者が短いから乱数が少ないわけではなく、表記の効率が違います。再生成すれば元の乱数も変わるので、二回実行した文字列が同じ内容の別表記になるとは限りません。ここで比べるのは、指定バイト数と出力文字数の関係です。
このツールは暗号学的な乱数を利用し、使えない環境でMath.randomへ切り替えて生成を続けません。ただし、生成した値をどう配布・保存・失効させるかまでは担当しません。招待や再設定リンクに必要な期限、一回限りの使用、受け付ける権限などはアプリケーション側の設計です。十分長い乱数を作れたことと、そのリンク全体が安全なことを同一視しないでください。
毎回同じテストをしたいときは、Seedの役割が変わる
画面の折り返しやデータ処理の不具合を再現するとき、毎回違う値は原因を追いにくくすることがあります。その場合はダミーデータ生成のSeedを使い、列定義、件数、Seedをそろえて同じ条件を再現します。これは予測できない秘密を作る目的とは反対です。Seedを知れば再現できる値を、パスワードや認証トークンに流用しないでください。
一方、乱数生成機能そのものをテストするなら、特定の文字列との一致だけを期待値にするのも不適切です。件数、形式、許された文字、版、指定したバイト数などを確認します。衝突が一度もなかったという小さな実験だけで、将来の重複が絶対にないとは証明できません。IDを保存する側では一意制約や衝突時の扱いも別に設計します。
人が入力する値には、別の読みやすさも必要
人が手入力するパスワードなら、0とO、1とlのような紛らわしい文字を避けたい場合があります。パスワード生成の文字種と除外設定を使い、入力先が受け付ける長さや記号を確認します。複数の文字種を必ず含める条件では、それぞれを入れられる長さも必要です。生成後に自分で規則的な文字を付け足すより、必要な条件を生成時に指定する方が、条件を一貫して管理できます。
最後に、用途を一文で決めます。『レコードを識別する』ならUUID、『推測しにくい秘密値を作る』なら必要なバイト数のトークン、『同じ不具合を再現する』ならSeed付きの架空データです。いずれも本番の個人情報をコピーしてテストデータを作る必要はありません。生成結果を共有する際も、その値がただの識別子なのか秘密なのかを確認し、クリップボードや画面共有の扱いを変えてください。
IDが違うことと、テストの状態が違うことは別
百個のUUIDを作れば百件の異なる識別子を持つデータを用意できます。しかし、全部が同じ名前、同じ状態、同じ長さなら、一覧画面の多様な条件を試したことにはなりません。空の名前、非常に長い名前、同時刻の並び、同じ表示名を持つ別IDなど、画面が扱う状態は別に設計します。識別子の生成はテストデータ作りの一部であり、件数を増やすことだけで境界値の確認が増えるわけではありません。再現が必要な不具合では、生成結果を明示的なFixtureへ固定する方法も考えます。
UUIDの形式が正しいことも、そのIDのレコードが存在することの確認ではありません。形式確認に通った架空IDを使い、存在しない場合の画面を試すこともできます。逆に短い連番へ勝手に置き換えると、実際のIDの長さによる折り返しや入力制限を見逃します。何を確かめる試験かに合わせ、IDの形式、存在有無、表示長、再現性を分けて選びます。パスワードやアクセス用トークンへ流用する場合は、識別子としての目的と秘密としての要件を混同しないでください。
テスト用に配布する一覧へ、本番の利用者IDや実トークンを混ぜないようにします。見た目の形式を合わせるためなら、架空の値で十分な場合が多くあります。生成した値をコピーするときは、その値が何のためのものかも分かる名前を添えると、後から本物の認証情報と取り違えにくくなります。ツールは入力や結果を自動保存しないので、再現に必要な値を残すかどうかは利用者の明示操作で決めます。ランダム生成、再現用データ、実運用の秘密はそれぞれ別の用途として扱います。