現代のウェブアプリケーション開発において、セキュリティの重要性はかつてないほど高まっています。
2026年現在、サイバー攻撃の手法はより高度化しており、安易な乱数生成アルゴリズムを用いたパスワード生成は大きなリスクを伴います。
Pythonでセキュリティに関わる乱数を扱う際には、標準ライブラリであるsecretsモジュールの活用が推奨されています。
本記事では、なぜ従来のrandomモジュールでは不十分なのか、そしてsecretsモジュールを使用してどのように安全なパスワードを生成すべきかについて、具体的なコード例を交えて解説します。
Pythonのsecretsモジュールとは
secretsモジュールは、Python 3.6で導入された比較的新しい標準ライブラリです。
このモジュールの主な目的は、パスワード、アカウント認証、セキュリティトークンなどの機密情報を管理するために適した、暗号学的に強い乱数を生成することにあります。
OSが提供する最も信頼性の高い乱数生成器にアクセスするため、予測不可能な数値を生成できる点が最大の特徴です。
2026年のシステム開発においても、セキュリティに関連する実装ではこのモジュールの使用がデファクトスタンダードとなっています。
randomモジュールを使用してはいけない理由
Pythonには古くからrandomというモジュールが存在しますが、これをセキュリティ目的で使用することは厳禁です。
randomモジュールは、メルセンヌ・ツイスタと呼ばれるアルゴリズムに基づいて疑似乱数を生成しています。
このアルゴリズムは決定論的であり、過去に生成された数値のパターンを一定数観測することで、将来生成される数値を完全に予測することが可能です。
シミュレーションやモデリングなどの統計的用途には適していますが、攻撃者に次の数値を推測される可能性があるため、パスワード生成には適していません。
一方でsecretsモジュールは、暗号学的に安全な疑似乱数生成器(CSPRNG)を採用しています。
これにより、生成されるデータに偏りがなく、外部から次の値を予測することは事実上不可能となっています。
secretsモジュールの基本的な関数
secretsモジュールには、用途に応じた複数の便利な関数が用意されています。
ここでは、パスワード生成やトークン発行によく使われる主要な機能を紹介します。
secrets.choice:リストから安全に要素を選択する
指定したシーケンス(リストや文字列など)から、暗号学的に安全な方法で要素を1つ選択します。
パスワードに使用する文字セットからランダムに文字を抽出する際に最も多用される関数です。
secrets.randbelow:範囲を指定した乱数生成
0から指定した値(その値自体は含まない)までの範囲で、安全な整数を生成します。
特定の数値範囲でパスコードを作成したい場合に適しています。
secrets.token_hex:16進数の文字列生成
指定したバイト数分のランダムな値を生成し、それを16進数の文字列として返します。
パスワードリセット用のトークンや、セッションIDの生成に非常に便利です。
secrets.token_urlsafe:URLに適した文字列生成
Base64エンコードされたランダムな文字列を生成しますが、URLでそのまま使用できるように「-」や「_」などの安全な文字のみを使用します。
一時的な認証用URLやAPIキーの生成に最適です。
実用的なパスワード生成プログラムの実装
それでは、実際にsecretsモジュールを使用して、セキュアなパスワードを生成するコードを書いてみましょう。
まずは、英数字を組み合わせたシンプルなパスワード生成の例です。
import secrets
import string
def generate_simple_password(length=12):
# 使用する文字の種類(英大文字、英小文字、数字)を定義
characters = string.ascii_letters + string.digits
# 指定された長さ分、安全に文字を選択して結合
password = ''.join(secrets.choice(characters) for i in range(length))
return password
# 16桁のパスワードを生成
new_password = generate_simple_password(16)
print(f"生成されたパスワード: {new_password}")
生成されたパスワード: 4kP9zR2mNqV8bX7t
このコードでは、string.ascii_letters(英字)とstring.digits(数字)を組み合わせています。
secrets.choiceを使用することで、各文字が選ばれる確率は等しく、かつ予測不可能な状態が保たれます。
記号を含む高度なパスワード生成
より強固なセキュリティが求められる場合、記号を混ぜるだけでなく「必ず1文字以上は数字と記号を含む」といった制約を設けることが一般的です。
以下の例では、特定の条件を満たすまで生成を繰り返す、より実戦的なロジックを紹介します。
import secrets
import string
def generate_secure_password(length=16):
# 文字セットの定義
alphabet = string.ascii_letters + string.digits + string.punctuation
while True:
# パスワードを生成
password = ''.join(secrets.choice(alphabet) for i in range(length))
# 強度チェック:小文字、大文字、数字、記号がそれぞれ1文字以上含まれているか
if (any(c.islower() for c in password)
and any(c.isupper() for c in password)
and sum(c.isdigit() for c in password) >= 2
and any(c in string.punctuation for c in password)):
break
return password
# 高強度なパスワードを表示
print(generate_secure_password(20))
j7*F#nQ2v9[LpW!zR4sT
このプログラムでは、whileループを使用してポリシーに適合するまで再生成を行っています。
これにより、偶然にも記号が含まれなかったというような、脆弱なパスワードが払い出されるリスクを回避できます。
URLセーフなトークンの生成方法
ユーザー登録時の確認メールに記載するURLなど、パスワード以外の用途でもsecretsは活躍します。
従来のuuidモジュールも一意性は高いですが、暗号学的な強さを求めるならtoken_urlsafeがベストです。
import secrets
# 安全な認証用URLトークンを生成
auth_token = secrets.token_urlsafe(32)
url = f"https://example.com/verify?token={auth_token}"
print(f"発行されたURL: {url}")
発行されたURL: https://example.com/verify?token=E9f-jX1q-WzV9_mK8aP2L5nBq9Z4rT7sX0vC3eY6gI8
引数に渡している「32」はバイト数であり、文字列の長さではない点に注意してください。
Base64エンコードされるため、実際の文字列長は約1.3倍になります。
十分な長さのトークンを生成することで、総当たり攻撃(ブルートフォース攻撃)を物理的に不可能にすることが可能です。
パスワード強度とエントロピーの考え方
セキュアなパスワードを生成する際に考慮すべきなのが「エントロピー(不規則性の度合い)」です。
エントロピーが高ければ高いほど、攻撃者がパスワードを推測するのに必要な計算量は膨大になります。
以下の表は、パスワードの長さと使用文字種による安全性の違いをまとめたものです。
| 文字セット | 推奨される長さ | セキュリティレベル |
|---|---|---|
| 数字のみ | 12文字以上 | 低い(PIN用途) |
| 英数字(大文字小文字) | 12〜16文字 | 標準的 |
| 英数字 + 記号 | 16文字以上 | 高い |
2026年の基準では、重要なシステムにおいては16文字以上の英数字と記号の組み合わせが推奨されます。
secretsモジュールを使えば、このような複雑なパスワードも数行のコードで簡単に生成できます。
運用の際の注意点
secretsモジュールは非常に強力ですが、プログラムの実行環境にも依存します。
このモジュールは内部的にos.urandom()を呼び出しており、OSが提供するエントロピー源(Linuxでは/dev/urandomなど)を使用します。
万が一、OSレベルで乱数生成器が利用できない特殊な環境(極めて制限された一部の組み込み環境など)では、RuntimeErrorが発生する可能性があります。
しかし、一般的なサーバー環境やクラウドプラットフォーム、モダンなデスクトップOSであれば問題なく動作します。
また、生成したパスワードをデータベースに保存する際は、そのまま保存せず、必ず強力なハッシュアルゴリズム(Argon2やbcryptなど)でハッシュ化して保存するようにしてください。
まとめ
Pythonで安全なパスワード生成を行うなら、randomモジュールではなく、必ずsecretsモジュールを使用しましょう。
secrets.choiceやsecrets.token_urlsafeを活用することで、暗号学的に安全で予測不可能なランダム値を簡単に得ることができます。
本記事で紹介したように、文字セットを適切に組み合わせ、十分な長さを確保することがセキュリティの第一歩です。
プログラムの堅牢性は、使用するライブラリの正しい選定から始まります。
2026年以降のシステム開発においても、ユーザーの安全を守るためにsecretsモジュールを正しく使いこなしてください。
