【実務・中級編】HTTP Digest認証のチャレンジ・レスポンスメカニズム – HTTPプロトコル・通信規格実践ガイド

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のアクセストークン発行フローでまた会おう。健闘を祈る。

コメント

タイトルとURLをコピーしました