認証の「穴」を塞ぐ:現場で通用するセッション管理とパスワードハッシュの鉄則
現場でコードを書いていて、「ログイン機能なんてライブラリ任せで十分」と思っていないか?
残念だが、その「ライブラリのデフォルト設定」が、攻撃者にとっての扉を開け放つ招待状になっていることが少なくない。
今日は、OWASP Top 10の常連である「認証の不備(Broken Authentication)」を撲滅するために、教科書的な知識ではなく、「攻撃者がどこを突くか」という視点から、堅牢な実装の勘所を叩き込む。
—
1. なぜ「セッションIDの固定化」は今も死なないのか
攻撃者は、ログイン前のセッションIDを盗み出し、被害者がログインした後にそのIDをそのまま使い回す(セッション固定化攻撃)。これを防ぐための鉄則はただ一つ。「権限が切り替わる瞬間(ログイン時)に、必ずセッションIDを再発行すること」だ。
実装例:PHPでセッションを再生成する
ログイン処理の冒頭で `session_regenerate_id(true)` を呼ぶ。これだけで、攻撃者が持っていた古いセッションIDは無効化される。
Nginx設定例:
開発者はこの設定を必ず確認せよ
add_header Set-Cookie “Path=/; HttpOnly; Secure; SameSite=Lax”;
—
2. パスワードハッシュ:時代遅れのMD5/SHA1は「即刻破棄」
今どき「`sha256(password + salt)`」で満足しているなら、明日のインシデント対応の準備をしておけ。GPUクラスタを回す攻撃者にとって、SHA系アルゴリズムは秒速数億回の計算が可能だ。
現在、我々が選択すべきは Argon2id 一択。これがない環境なら bcrypt で妥協しろ。これらは「計算負荷(メモリ消費量や反復回数)」を調整できるため、攻撃側のコストを跳ね上げることができる。
実装例:Python (bcrypt) による堅牢なハッシュ化
パスワード比較には必ず `checkpw` を使うこと。自前で `==` 演算子を使って比較してはいけない(タイミング攻撃でCPUの比較時間を計測され、パスワードを推測されるリスクがあるため)。
import bcrypt
パスワード保存時
def hash_password(password: str):
# 負荷係数を指定(ワークファクター)
# 12程度が、現在の計算リソースにおけるバランスの良い値
salt = bcrypt.gensalt(rounds=12)
return bcrypt.hashpw(password.encode(‘utf-8’), salt)
ログイン検証時
def verify_password(password: str, hashed: bytes):
# 定数時間で比較されるためタイミング攻撃に強い
return bcrypt.checkpw(password.encode(‘utf-8’), hashed)
—
3. ブルートフォース攻撃を止める「泥臭い」防衛策
ログイン画面にレートリミットをかけないのは、空き巣に玄関の鍵を開けたままにしておくようなものだ。
WAFとアプリケーション層の二段構え
1. WAF側: 特定のIPアドレスからの異常なログイン試行を遮断する。
2. App側: ユーザーIDごとのログイン失敗回数をRedis等でカウントし、一定回数で「アカウントロック」または「ログインまでの待ち時間」を強制する。
Redisを使ったログイン試行制限のロジック(概念コード):
def check_login_attempt(user_id):
# 5回失敗したら15分間ロック
attempts = redis.get(f”login_fail:{user_id}”)
if attempts and int(attempts) >= 5:
raise Exception(“アカウントが一時的にロックされました”)
def record_fail(user_id):
key = f”login_fail:{user_id}”
redis.incr(key)
redis.expire(key, 900) # 15分後にカウントリセット
—
最後に:セキュリティは「完璧」を目指すな
どれだけ強固なパスワードハッシュやセッション管理を実装しても、脆弱性は必ずどこかに出る。だからこそ、「認証イベントをすべてログに吐き出す」ことこそが最後の砦だ。
誰が、いつ、どこからログインし、いつ失敗したか。このログがなければ、インシデント発生時に我々のようなセキュリティエンジニアも打つ手がない。
「動く」ことは最低条件。その後ろに「なぜ安全と言えるのか」という論理武装を必ず用意せよ。
これが、現場のエンジニアが持つべきプロフェッショナリズムだ。次は、君たちのチームでこの実装を確認することから始めてくれ。

コメント