Q1パスワードリセットトークン を生成するときに使うべきモジュールはどれですか?
random と secrets — 乱数の使い分け
Python の random / secrets モジュールを基礎から解説します。random.choice / randint / shuffle と seed による再現可能な疑似乱数、secrets.token_hex / token_urlsafe による暗号学的に安全なトークン、テスト用とセキュリティ用の使い分け基準まで、ハンズオンで学べます。
乱数を扱う 2 モジュール を整理します。random は テスト・ゲーム・シミュレーション用の疑似乱数(seed で再現可能)、secrets は パスワードリセットトークン・API キーなどセキュリティ用の予測不能な乱数(OS 提供の暗号乱数源)。用途で必ず使い分ける のが鉄則です。
random と secrets の違い
両モジュールは「乱数を返す」点では同じですが、仕組みと用途が完全に分かれています。random は メルセンヌ・ツイスター(広く使われている疑似乱数生成アルゴリズム)で、random.seed(値) を呼べば 同じ並びを再現 できます。一方 secrets は OS が提供する暗号乱数源(/dev/urandom 等)から取得するので、`seed` の概念はなく、毎回予測不能 な値が返ります。
テストやゲームのように「結果が外部に漏れても被害が出ない」場面では random、パスワード・トークン・セッション ID のように「外部に予測されたら被害が出る」場面では secrets を使う、と覚えてください。
| 観点 | random | secrets |
|---|---|---|
| 乱数源 | メルセンヌ・ツイスター (アルゴリズム) | OS の暗号乱数源 (/dev/urandom 等) |
| 再現性 | seed で同じ並びを再現できる | 再現不可 (毎回違う値) |
| 速度 | 高速 | 比較的遅い (用途的に問題にならない) |
| 向く用途 | テスト・ゲーム・シミュレーション | トークン・パスワード・セッション ID |
random — 基本の乱数 (randint / uniform / choice)
random モジュールの基本は 整数の乱数 / 小数の乱数 / リストから 1 件選ぶ の 3 つです。random.randint(下限, 上限) で 両端を含む整数、random.uniform(下限, 上限) で 指定範囲の float、random.choice(リスト) で リストから要素を 1 つ取り出す ことができます。
まずは seed を使わずに、これらの基本関数の振る舞いを確認します。
| 関数 | 戻り値 | 使いどころ |
|---|---|---|
| random.randint(a, b) | a 〜 b の整数 (両端含む) | サイコロ、テスト用 ID |
| random.uniform(a, b) | a 〜 b の float | ノイズ、確率的シミュレーション |
| random.choice(seq) | シーケンスから 1 つ | メニューから 1 件抽出 |
random — シードによる再現性とコレクション操作
ここからは random.seed によるテスト再現性と、コレクション操作系の関数(shuffle / sample)を扱います。random.seed(N) を呼んでから乱数関数を使うと 同じシード値なら必ず同じ並びの値 が出ます。random.shuffle(リスト) は 元のリストの並びをランダムに入れ替え、random.sample(リスト, k) は 重複なしで k 件取り出した新しいリスト を返します。
| 関数 | 戻り値 | 使いどころ |
|---|---|---|
| random.seed(value) | None (内部状態を初期化) | テストの再現性確保 |
| random.shuffle(seq) | None (元のリストを並べ替える) | リストの順番をランダムに入れ替える |
| random.sample(seq, k) | 重複なし k 件のリスト | アンケートから k 人抽選 |
secrets — セキュリティに使える強い乱数
前セクションの random は テスト用の疑似乱数 で、内部状態が漏れると次の値が予測されてしまいます。パスワードリセットトークン・API キー・セッション ID のような「攻撃者に予測されたら被害が出る用途」では、OS 提供の暗号学的乱数源を使う secrets モジュール を選びます。
secrets の API は random よりずっと小ぶりで、ランダムなバイト列を 16 進文字列や URL セーフな文字列に変換するヘルパー関数 が中心です。
| 関数 | 戻り値 | 使いどころ |
|---|---|---|
| secrets.token_bytes(n) | n バイトのランダムな bytes | 暗号鍵、内部用バイナリトークン |
| secrets.token_hex(n) | 2*n 文字の 16 進文字列 | ログインセッション ID |
| secrets.token_urlsafe(n) | URL セーフな文字列 (Base64 風) | リセットリンクの URL に埋める |
| secrets.choice(seq) | seq から暗号学的に 1 件 | ランダム表示順を予測されたくない場面 |
| secrets.compare_digest(a, b) | True / False (タイミング攻撃耐性) | ハッシュ・トークン比較 (== の代わり) |
理解度チェック
まずは1問ずつ答えてみましょう。
Q2random.seed(42) を呼んでから random.randint(1, 100) を実行し、もう一度同じことをする と何が起きますか?
Q3ランダムに並び替えたリストをテスト用に再現できるようにしたい とき、最も適切なのはどれですか?