パスワードに関する一般的なアドバイスの多くは間違っているか、非実用的です。「大文字・小文字・数字・記号を混ぜて使いなさい」という指示は厳格に聞こえますが、P@ssw0rd1!のようなパスワードを生み出します。これはコンピュータにとって簡単に解析できる反面、人間には覚えにくいものです。本ガイドでは本質を解説します。

パスワードの強度を決めるもの

パスワードの強度は根本的にはエントロピーの問題です — 攻撃者がパスワードを見つけるのに必要な試行回数です。

Tr0ub4dor&3のような12文字のパスワードは複雑に見えます。しかし、一般的なパターン(単語+数字置換+記号)に従っているため、ルールベースの変形を使った辞書攻撃でランダムな8文字パスワードより速くクラックされることがあります。

真のランダム性は、見た目の複雑さより常に優れています。

パスワードエントロピー

エントロピーはビット単位で予測不可能性を測ります。1ビット増えるごとに探索空間は2倍になります。

N文字のプールからL文字のパスワードを選ぶ場合:

エントロピー = L × log₂(N)
文字プールプールサイズ12文字のエントロピー
小文字のみ2656ビット
小文字+大文字5268ビット
小文字+大文字+数字6272ビット
小文字+大文字+数字+記号(32)9479ビット
パスフレーズ(7776語のワードリスト)7776約51ビット/語

参考:

  • 50ビット未満:現代のハードウェアで数秒〜数時間でクラック可能
  • 50〜70ビット:レート制限があれば低リスクアカウントには十分
  • 70ビット以上:ほとんどの用途で強力
  • 100ビット以上:将来にわたって安全

強力なパスワードを生成する

ZeroTool パスワードジェネレーター →

ブラウザのcrypto.getRandomValues() APIを使用して暗号論的にランダムなパスワードを生成します。長さ・文字セット・生成数のオプション付き。データはサーバーに送信されません。

パスワードジェネレーターの仕組み

安全なパスワードジェネレーターは**暗号論的安全な疑似乱数生成器(CSPRNG)**を使用する必要があります。標準的なRNGではいけません。

この違いは重要です:

// 誤り:Math.random()は暗号論的に安全ではない
// 攻撃者がシードを知っていれば、全出力を予測できる
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let password = '';
for (let i = 0; i < 16; i++) {
    password += chars[Math.floor(Math.random() * chars.length)];
}

// 正しい:crypto.getRandomValues()は暗号論的に安全
function generatePassword(length = 16, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*') {
    const array = new Uint32Array(length);
    crypto.getRandomValues(array);
    return Array.from(array, n => charset[n % charset.length]).join('');
}

Pythonの場合:

import secrets
import string

def generate_password(length=16, use_symbols=True):
    alphabet = string.ascii_letters + string.digits
    if use_symbols:
        alphabet += string.punctuation

    return ''.join(secrets.choice(alphabet) for _ in range(length))

# secretsモジュールはos.urandom()を使用 — 暗号論的に安全
password = generate_password(20)

注意:Pythonのrandom.choice()は暗号論的に安全ではありません。セキュリティが重要なランダム性には常にsecrets.choice()を使用してください。

パスフレーズ:ランダム文字列の代替

パスフレーズはランダムな辞書の単語を並べたものです:

correct-horse-battery-staple
violet-mountain-cloud-seven-river

7776語のリスト(Diceware)から各単語を選ぶと、約12.9ビットのエントロピーが加わります。4語で約52ビット、5語で約64ビット、6語で約77ビットです。

メリット:

  • モバイルでの入力が楽
  • 時折覚える必要がある場合に記憶しやすい
  • 視覚的に区別しやすい

デメリット:

  • 長い(より多くの文字を入力する必要がある)
  • 長いパスフレーズを壊す文字数制限があるシステムも存在する

長さと複雑さのどちらが大切か

長くするか複雑にするかを選択しなければならない場合、長さを選んでください。1文字追加するごとに探索空間はプールサイズ(26〜94倍)だけ増加します。すでに小文字・大文字・数字を使っている場合に特殊記号を追加しても、16文字のパスワードでは3ビットしか増えません。

実践的な推奨:16文字以上の混合文字クラスを使用してください。

パスワードを記憶しようとしないこと

アカウントごとにユニークなランダムパスワードを生成していれば(そうすべきです)、それらを暗記することはできません。これがパスワードマネージャーが存在する理由です。

パスワードマネージャーは、すべてのパスワードを1つのマスターパスワードで暗号化して保存します。強力なパスフレーズを1つ覚えれば、後はマネージャーが処理します。

おすすめのツール:

  • Bitwarden — オープンソース、無料プランが優秀、セルフホスト可能
  • 1Password — 洗練されたUX、チーム共有機能が充実
  • KeePassXC — ローカルのみ、クラウド依存なし

パスワードの使い回しは絶対にしないでください。1つのデータ侵害で、使い回したすべてのアカウントが露出します。

開発者向け:シークレットの生成

アプリケーションシークレット(APIキー・JWTシークレット・セッションキー・暗号化キー)には、プラットフォームの安全なランダムバイト関数を使用してください:

# シェル — 32ランダムバイトを16進数で(ほとんどのシークレットに適切)
openssl rand -hex 32

# シェル — base64エンコード
openssl rand -base64 32
import secrets

# 32バイトの16進数シークレット(64文字)
api_key = secrets.token_hex(32)

# URLセーフなbase64シークレット
session_secret = secrets.token_urlsafe(32)
// Node.js
import { randomBytes } from 'crypto';
const apiKey = randomBytes(32).toString('hex');

JWTシークレットには少なくとも256ビット(32バイト)を使用してください。AES-256には正確に32バイトが必要です。人間が記憶できる文字列を暗号鍵として使用してはいけません。

Have I Been Pwned との連携

Have I Been Pwned(HIBP)APIを使うと、実際のパスワードを送信せずに、パスワードが既知のデータ侵害に登場しているかチェックできます:

  1. パスワードのSHA-1ハッシュを計算
  2. ハッシュの最初の5文字だけをAPIに送信
  3. APIはその5文字で始まるすべてのハッシュを返す
  4. フルハッシュがリストにあるかをローカルで確認
import hashlib
import requests

def is_pwned(password):
    sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
    prefix, suffix = sha1[:5], sha1[5:]

    response = requests.get(f'https://api.pwnedpasswords.com/range/{prefix}')
    hashes = (line.split(':') for line in response.text.splitlines())
    return any(h == suffix for h, _ in hashes)

if is_pwned("P@ssw0rd1"):
    print("このパスワードは既知の侵害に登場しています!")

HIBPデータベースに登場するパスワードは、見た目の複雑さにかかわらず絶対に使用しないでください。

まとめ

目標推奨事項
ランダムパスワード16文字以上、混合文字セット
記憶可能なパスフレーズDicewareから6語以上
アプリケーションシークレットsecrets.token_hex(32) または openssl rand -hex 32
パスワード保存Argon2id(3回以上のイテレーション、64MBメモリ)
侵害チェックHave I Been Pwned API
日常のパスワード管理Bitwarden または 1Password

エントロピー・ランダム性・一意性 — 恣意的な複雑さルールではなく、これらこそがパスワードを安全にするものです。

ZeroTool で暗号論的に強力なパスワードを生成 →