読者です 読者をやめる 読者になる 読者になる

うならぼ

どうも。

Digest認証のパラメータを眺めた

「鯖からのnonceを含めたハッシュを投げて認証するやつでしょ?」

と思ったら意外と沢山パラメータがあったので、一通り読み解いてみた。

鯖→蔵:WWW-Authenticate ヘッダ

// RFC2617 §3.5 より引用

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest
    realm="testrealm@host.com",
    qop="auth,auth-int",
    nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
    opaque="5ccc069c403ebaf9f0171e9517f40e41"

realm(必須)

Basic認証同様にパスワードダイアログに表示される説明であると同時に、ハッシュ計算にも用いられる。RFC2617では例として「認証を行うホスト名」や「誰がアクセスできるものなのか」を挙げている。

とはいえ registered_users@gotham.news.com は違和感があるのだけど。

domain

ドメイン名のことではなく、完全なURIもしくは絶対パスのリスト。ここで指定したURI(かつ同じrealm)なら同じ資格情報が使える、という情報になる。省略された場合はレスポンスを返したサーバー全体として扱うべきとされている。

nonce(必須)

Digest認証の要。少なくとも認証しなおす(HTTP401など)ときには更新すべき。例ではタイムスタンプとETagと秘密鍵を使って生成している。

ワンタイムトークンのようにリクエストごとに更新しても構わない。その場合 Authentication-Info ヘッダで次の nonce を通知することもできる。……とはいえ、静的リソースも含めた全リクエストに対してそれをやるのはパフォーマンスの問題があるほか、パイプライン処理などによる同時リクエストも難しくなる。

リプライ攻撃への対策としては nc (nonce-count) パラメータもある。

opaque

いわゆる「ユーザー定義のデータ」。クライアントが同じ認証領域内のURIにアクセスする際、常にAuthorizationヘッダに付加される。

stale

受け取ったAuthorizationヘッダについて、nonce は無効だがユーザー名・パスワードを含むハッシュは正しいという時にtrueになる。どこかから盗んできたnonceかもしれないし、単にタイムアウトしただけかもしれない。

この場合、クライアントはユーザーに尋ねることなく同じ資格情報と新しいnonceで認証しなおせばよい。(元の資格情報を持っていなければここで脱落する)

algorithm

RFC2617で定義されているのは MD5MD5-sess のみ。このうちどれをサポートするかを示す。ただ SHA-256 を使えるようにという動きがあるらしい。

HTTP Digest 認証の SHA-256 の扱い - Tociyuki::Diary

Authorization ヘッダの response は user:realm:passwdハッシュ値をその他のパラメータと連結してまたハッシュ値を計算するのだが、 MD5-sess の場合は前述のハッシュ値にさらに nonce/cnonce を連結してハッシュしたものを使う。このメリットが今一つ理解できていないが、外部の認証プロバイダと連携する際に便利と書かれているので、より限定的なキーを生成(そして保持)できるようにということだろうか。

qop (qop-options)

authauth-int のうちどれをサポートするかを示す。auth-int の場合、リクエストボディもハッシュ計算に使われる。

旧版である RFC2069 との互換性のために optional となっているものの、現在では常に使用すべきとされている。このパラメータが省略された場合は RFC2069 互換で動くため、クライアント側も cnonce や nonce-count を使わない。

蔵→鯖:Authorization ヘッダ

// RFC2617 §3.5 より引用

Authorization: Digest username="Mufasa",
    realm="testrealm@host.com",
    nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
    uri="/dir/index.html",
    qop=auth,
    nc=00000001,
    cnonce="0a4f113b",
    response="6629fae49393a05397450978507c4ef1",
    opaque="5ccc069c403ebaf9f0171e9517f40e41"

username は言わずもがな。realm、nonce、algorithm、opaque、qop は上で説明しちゃったので省略。response の生成方法は RFC 見たほうが早いと思うの。

uri(必須)

リクエストのURIをそっくりそのまま。プロキシが勝手に書き換えないように、ということらしい。

cnonce

クライアント側で生成する nonce。このパラメータを付けることで選択平文攻撃への耐性が上がる。要するに、悪意を持った第三者が鯖になりすまして nonce 等々を制御できたとしても cnonce は予測できないので、パスワードの推測が困難になる。

Do Client Nonces enhance the security of HTTP Digest Auth?

nc (nonce-count)

同じ nonce を使ってリクエストを発行するごとにインクリメントされる値。これも response の計算に使われる。

順番の乱れや抜け落ちを検知するためではなく、同じ nc のリクエストを受け取った際に重複として弾くことを目的としている。とはいえサーバー側の挙動に規定はないので、サーバー側でインクリメントした値と比較する、なんて実装もありと言えばあり。うん、まあ。

参考サイト

謝辞

この記事を、一見しっかり実装されているように見えてnonce-countの扱いが微妙なphp-http-digest-authと、stale実装してないのにタイムアウト強制するZendFrameworkに捧げます。