HTTP Digest認証の深層:なぜ「パスワードをそのまま送る」のが悪手なのか
ネットワークの現場に長く立っていると、若手から「Basic認証でSSLを通してるから安全ですよね?」という質問を受けることがよくある。答えは「半分正解だが、半分は甘い」。Basic認証はBase64でエンコードされているだけで、実質的には平文だ。通信経路上で中間者攻撃(MITM)に遭えば、認証情報は一瞬で露呈する。
今回は、そんなBasic認証の脆弱性を補完するために設計された「HTTP Digest認証」について、パケットの裏側を覗きながら深掘りしていこう。
—
1. パスワードを晒さない「魔法」の正体
Digest認証の最大の功績は、「パスワードそのものをネットワーク上に流さない」ことにある。代わりに、パスワードをハッシュ化し、サーバー側から送られてくる「nonce(ナンス:使い捨ての値)」と混ぜ合わせることで、唯一無二の認証トークンを作り出す。
通信シーケンスの核心
1. クライアント: リクエストを投げる。
2. サーバー: `401 Unauthorized` を返し、`WWW-Authenticate` ヘッダーに `nonce` や `realm` を載せて「これで署名を作れ」と指示する。
3. クライアント: パスワード、nonce、URLなどを組み合わせてMD5ハッシュを生成し、`Authorization` ヘッダーに入れて再送する。
4. サーバー: サーバー側でも同じ計算を行い、一致すれば `200 OK` を返す。
この仕組みにより、仮に攻撃者が通信を傍受しても、手に入るのは「一度しか使えない署名」だけであり、元のパスワードを逆算することは困難だ。これが「チャレンジ・レスポンス」の基本形である。
—
2. なぜMD5なのか、そして何が危険なのか
ここでシニアエンジニアとして一つ警告しておきたい。RFC 2617で定義されたDigest認証は、ハッシュアルゴリズムとしてMD5に強く依存している。
現代の計算能力を持ってすれば、MD5の衝突耐性の低さは致命的だ。もしシステムがオフライン攻撃(パスワードハッシュを直接盗み出されるケース)を受けた場合、MD5は瞬時に解析される。そのため、Digest認証を採用するなら、「リプレイ攻撃対策」としてのnonceのライフタイム管理を、サーバー実装側で厳格に行う必要がある。
—
3. 実践:Digest認証のハンドシェイクを再現する
では、実務でこの認証をどう扱うか。`curl` を使えば、一瞬でDigest認証の裏側が可視化できる。
curlで挙動を追う
-v オプションでヘッダー情報をすべて表示させる
–digest フラグを付けるだけで、curlが自動的にnonceを処理してくれる
curl -v –digest -u “user:password” http://example.com/api/resource
このコマンドを叩くと、コンソールには以下のやり取りが流れるはずだ。
- サーバーからの要求(401):
`WWW-Authenticate: Digest realm=”Restricted”, qop=”auth”, nonce=”dcd98b7102dd2f0e8b11d0f600bfb0c093″`
- クライアントからの回答:
`Authorization: Digest username=”user”, realm=”Restricted”, nonce=”dcd98b7102dd2f0e8b11d0f600bfb0c093″, uri=”/api/resource”, qop=auth, nc=00000001, cnonce=”…”, response=”a1b2c3d4…”`
この `response` フィールドこそが、すべてを握る鍵だ。
—
4. Pythonによる実装のヒント
もしWeb APIのクライアントを実装する必要があるなら、`requests` ライブラリの `HTTPDigestAuth` を使うのが定石だ。車輪の再発明は避けよう。
import requests
from requests.auth import HTTPDigestAuth
URLと認証情報
url = “http://example.com/api/resource”
auth = HTTPDigestAuth(‘user’, ‘password’)
リクエストを送る際、自動的に401を検知して再送してくれる
response = requests.get(url, auth=auth)
if response.status_code == 200:
print(“認証成功!”)
else:
print(f”ステータスコード: {response.status_code}”)
—
5. 現場のシニアからのアドバイス:運用上の落とし穴
最後に、運用フェーズで必ずハマるポイントを3つ伝授する。
1. nonceの枯渇と再利用: 高負荷な環境では、nonceの生成ロジックがボトルネックになる。クライアントが古いnonceを使い続けるとサーバー側で検証エラーになる。このログが大量に出る場合は、サーバーのnonce有効期限設定(`nonce-timeout`)を見直すべきだ。
2. HTTPSとの併用: 「Digest認証は安全」と過信してHTTPで運用するのはNGだ。前述の通りMD5の脆弱性があるため、必ずTLS(HTTPS)で通信経路を保護した上で、多重防護としてDigest認証を使うのが現代の鉄則である。
3. デバッグの基本: Wiresharkでパケットを追う際は、`http.authbasic` ではなく `http.authdigest` でフィルタリングすること。`Authorization` ヘッダーの中身がデコードできない場合は、クライアント側でのハッシュ計算ロジック(特に `nc` や `cnonce` の生成)に不備がある可能性が高い。
Digest認証は、Webの歴史が培った「パスワードを隠す」ためのエレガントな工夫だ。現在の認証標準であるOAuth 2.0やOpenID Connectの影に隠れがちだが、組み込み機器やシンプルなAPI連携では今なお現役だ。仕組みを理解し、適切に使いこなすことで、あなたの作るシステムは一歩上の堅牢さを手に入れるだろう。
次は、OAuth 2.0のアクセストークン発行フローでまた会おう。健闘を祈る。
コメント