Pythonの標準ライブラリだけで、パスワード生成に必要なものは全部揃います。pip installは不要。この記事では、基本的なランダム文字列から実務レベルの一括生成まで、10パターンのパスワード生成コードを紹介します。すべてのコードはPython 3.6以降でそのまま動きます。
最初にひとつだけ。セキュリティ関連の値を生成するときはrandomではなくsecretsを使いましょう。見た目はほぼ同じですが、randomはシミュレーション用で、認証情報の生成には向いていません。見た目が似ているからこそ、間違えやすいポイントです。
1. 基本形:ランダムパスワード
英小文字・大文字・数字・記号から、指定した長さのパスワードを生成する最もシンプルな形です。
import secrets
def generate_password(length=16):
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%"
return "".join(secrets.choice(chars) for _ in range(length))
if __name__ == "__main__":
for i in range(5):
print(f"Password {i+1}: {generate_password()}")
十分実用的ですが、「必ず英大文字・数字・記号を含む」とは限りません。文字集合には含まれていても、たまたま数字が1文字も入らないことは普通にあります。サービス側に「記号必須」等の条件があるなら、パターン5の方が確実です。
2. 紛らわしい文字を除外
0とO、1とlのような、見分けがつきにくい文字を除外したい場合に便利です。
import secrets
import string
def generate_password(length=16, excluded="0OIl1"):
all_chars = string.ascii_letters + string.digits + "!@#$%"
chars = "".join(c for c in all_chars if c not in excluded)
if not chars:
raise ValueError("使用可能な文字がありません。")
return "".join(secrets.choice(chars) for _ in range(length))
if __name__ == "__main__":
for i in range(5):
print(f"Password {i+1}: {generate_password()}")
除外しすぎると候補数が減って強度が少し下がります。長さを1〜2文字増やせば簡単に補えますが、「見やすさ」と「総当たり耐性」は少し綱引きになります。
現場では「強いが読めないパスワード」よりも、少し長めで読み間違えにくいパスワードの方が運用全体として安全なことがあります。電話口で「大文字のアイです」「小文字のエルです」が始まると、だいたい平和が遠のきます。
3. プレフィックス付きランダム
USER_やAPIKEY_のような固定文字列の後ろに、ランダム文字列を付けたいときに使えます。
import secrets
import string
def generate_with_prefix(prefix="USER_", random_length=12):
chars = string.ascii_letters + string.digits
tail = "".join(secrets.choice(chars) for _ in range(random_length))
return prefix + tail
if __name__ == "__main__":
for i in range(5):
print(f"Key {i+1}: {generate_with_prefix()}")
接頭辞が固定なので、実質的なランダム部分の長さだけが強度を決めます。全体18文字でも、固定6文字+ランダム12文字なら、強度はランダム12文字相当です。プレフィックスはログの判別用であって、攻撃者を混乱させるものではありません。
4. プレフィックス付き一括生成
同じ接頭辞のキーやコードをまとめて複数件作りたい場合向けです。
import secrets
import string
def generate_batch(prefix="APIKEY_", random_length=16, count=5):
chars = string.ascii_letters + string.digits
return [
prefix + "".join(secrets.choice(chars) for _ in range(random_length))
for _ in range(count)
]
if __name__ == "__main__":
for i, key in enumerate(generate_batch(), 1):
print(f"Key {i}: {key}")
重複チェックは入っていません。ランダム部分が十分に長ければ衝突確率は天文学的に低いですが、数百万件レベルならパターン10の方が安心です。
大量発行で本当に怖いのは、重複そのものより「誰にどの値を渡したかの対応表が壊れること」だったりします。強いコードを作るより、生成した一覧を「とりあえずデスクトップに平文保存」しない方が、だいたい先に効きます。
5. 文字種必須の強力版
英大文字・小文字・数字・記号を必ず1文字以上含める、条件付きの強力パスワード生成です。ログインパスワード用途ではこのパターンがかなり扱いやすいです。
import secrets
import string
import random
def generate_strong_password(length=16):
if length < 4:
raise ValueError("length は 4 以上である必要があります。")
lower = string.ascii_lowercase
upper = string.ascii_uppercase
digits = string.digits
symbols = "!@#$%^&*"
parts = [
secrets.choice(lower),
secrets.choice(upper),
secrets.choice(digits),
secrets.choice(symbols),
]
all_chars = lower + upper + digits + symbols
parts += [secrets.choice(all_chars) for _ in range(length - 4)]
random.shuffle(parts)
return "".join(parts)
if __name__ == "__main__":
for i in range(5):
print(f"Password {i+1}: {generate_strong_password()}")
シャッフルにrandom.shuffle()を使っている点が気になるかもしれません。すでにランダムに選ばれた文字の「位置」を決めるだけなので実務上の影響はまずありませんが、厳密にそろえたければFisher–Yatesシャッフルをsecrets.randbelow()で回す手もあります。
よくあるミスは、先頭1文字目を必ず大文字、2文字目を必ず数字、のように位置を固定してしまうこと。規則性を作るのは、ランダム性の真逆です。
6. 読みやすさ重視版
紛らわしい文字と記号を除いた、人に伝えやすいパスワード。初期パスワードや一時発行コードとの相性が良い方式です。
import secrets
import string
def generate_readable(length=16):
excluded = "0OIl1"
chars = "".join(
c for c in string.ascii_letters + string.digits
if c not in excluded
)
return "".join(secrets.choice(chars) for _ in range(length))
if __name__ == "__main__":
for i in range(5):
print(f"Password {i+1}: {generate_readable()}")
セキュリティは理論値だけではなく、人間が正しく扱えるかでも決まります。最強の文字列でも、3回打ち間違えて付箋に書かれたら、だいぶ雲行きが怪しいです。
7. 単語つなぎパスフレーズ
ランダムな単語をつないで、覚えやすくて長い文字列を作ります。XKCD #936で有名になったアプローチです。
import secrets
WORDS = [
"river", "cloud", "apple", "stone", "forest",
"ocean", "light", "shadow", "iron", "spark",
"maple", "crane", "drift", "ember", "frost",
"bloom", "cedar", "ridge", "pearl", "storm",
]
def generate_passphrase(word_count=4):
return "-".join(secrets.choice(WORDS) for _ in range(word_count))
if __name__ == "__main__":
for i in range(5):
print(f"Passphrase {i+1}: {generate_passphrase()}")
サンプルの単語リストは意図的に短くしています。本気で使うならEFF Dicewareリスト(7,776語)のようなものを使いましょう。4語で約51ビットのエントロピーが得られます。
パスフレーズで一番やってはいけないのは、自分で単語を選ぶこと。「覚えやすいから」と好きな単語を並べた瞬間、推測されやすさが跳ね上がります。ランダム16文字は強いですが、毎日入力するには手首ではなく気力が先に疲れます。こういう場面では、パスフレーズの方が現実的な選択肢です。
8. URL安全トークン
APIキーやメール認証トークン、ワンタイムURLなどに向いた、URL内にそのまま埋め込めるランダム文字列です。
import secrets
import base64
def generate_token(byte_length=24):
raw = secrets.token_bytes(byte_length)
return base64.urlsafe_b64encode(raw).decode().rstrip("=")
if __name__ == "__main__":
for i in range(5):
print(f"Token {i+1}: {generate_token()}")
「URL安全」は「URLで扱いやすい」という意味であって、「漏れても安全」という意味ではありません。URLに含めたトークンは、ブラウザ履歴、サーバーログ、Refererヘッダーなどに残る可能性があります。可能であれば有効期限を短くしましょう。
9. 桁数・件数指定の一括生成
長さと件数を指定してまとめて作る、シンプルなユーティリティです。
import secrets
import string
def generate_batch(length=16, count=5):
chars = string.ascii_letters + string.digits + "!@#$%"
return [
"".join(secrets.choice(chars) for _ in range(length))
for _ in range(count)
]
if __name__ == "__main__":
for i, pw in enumerate(generate_batch(length=20, count=5), 1):
print(f"Password {i}: {pw}")
件数が多い場合は、標準出力ではなくファイルに直接書き出すことをおすすめします。ターミナルのスクロールバッファは意外としぶとく残りますし、共有セッションで500個のパスワードをスクロールバックされるのは、誰も嬉しくないサプライズです。
10. 重複なし一括生成
複数ユーザーに一括発行するときなど、重複を確実に避けたい場合のパターンです。
import secrets
import string
def generate_unique(length=16, count=10):
chars = string.ascii_letters + string.digits + "!@#$%"
result = set()
while len(result) < count:
result.add("".join(secrets.choice(chars) for _ in range(length)))
return list(result)
if __name__ == "__main__":
for i, pw in enumerate(generate_unique(length=16, count=5), 1):
print(f"Password {i}: {pw}")
16文字の英数字+記号なら衝突確率は天文学的に低いですが、length=4, count=100000のような極端な条件を渡すと、候補の母集団に対して要求件数が大きすぎてループが伸びます。入力値のサニティチェックを入れておくと安心です。
一括発行で本当に怖いのは、パスワードの重複ではなく、「誰にどのパスワードを渡したかの対応表が壊れること」です。乱数生成器は簡単な部分。台帳管理の方が先に機能停止しがちです。
まとめ
今回紹介した10パターンはすべてPython標準ライブラリ(secrets、string、random、base64)だけで動作します。追加のpip installは不要です。
ログインパスワードにはパターン5(文字種必須版)、APIトークンにはパターン8(URL安全版)、人間が毎日入力するマスターパスワードにはパターン7(パスフレーズ)がおすすめです。
そして忘れてはいけないのは、強いパスワードを生成するのは簡単な方だということ。安全に保存し、安全に渡し、適切なタイミングでローテーションする——本当の仕事はそこから始まります。

コメントを残す