Studying HTTP > RFC-Translations related HTTP

この文書は、 J. Franks, P. Hallam-Baker, J. Hostetler, S. Lawrence, P. Leach, A. Luotonen, L. Stewart: HTTP Authentication: Basic and Digest Access Authentication (RFC 2617), June 1999. を 橋本英彦 が日本語訳した物です。 この文書の取り扱いについては、[Studying HTTP] の RFC 日本語訳を利用するにあたってに従って下さい。


Network Working Group
Request for Comments: 2617
Obsoletes: 2069
Category: Standards Track
                           J. Franks
             Northwestern University
                     P. Hallam-Baker
                      Verisign, Inc.
                        J. Hostetler
                     AbiSource, Inc.
                         S. Lawrence
               Agranat Systems, Inc.
                            P. Leach
               Microsoft Corporation
                         A. Luotonen
 Netscape Communications Corporation
                          L. Stewart
                   Open Market, Inc.
                           June 1999

HTTP 認証: 基本アクセス認証及びダイジェストアクセス認証

この文書の位置付け

この文書は、インターネットコミュニティにおけるインターネット標準化過程プロトコルを規定し、改良のために議論と提案を求めるものである。 このプロトコルの標準化状態と状況については、"Internet Official ProtocolStandards" (STD 1) の最新版を参照していただきたい。 この文書の配布に制限は無い。

著作権表示

Copyright © The Internet Society (1999). All Rights Reserved.

概要

"HTTP/1.0" は、基本{Basic} アクセス認証スキームのための仕様を含んでいる。 このスキームは、ユーザー名及びパスワードが明文としてネットワーク上を通るので、(SSL [5] のような外部のセキュアなシステムと併用されるのでなければ) ユーザー認証の方法として安全であると考える事はできない。

またこの文書は、HTTP の認証フレームワークのための仕様である、元々の基本認証スキーム、及び "ダイジェスト{Digest} アクセス認証" と呼ばれる、暗号化ハッシュに基づくスキームを示すものである。 従って、これは RFC2069 [6] を置き換えるものとしてに意図される。 RFC 2069 にて示されたいくつかのオプショナルな要素は、その発行以降に発見された問題によりこの仕様から削除された; しかし互換性のために他の新しい要素が追加され、それら新しい要素はオプショナルとはなったが、強く推奨されるものである。

基本認証のように、ダイジェストアクセス認証は接続し合う両者が共有される秘密 (パスワード) を知っているという事を確認する; また基本認証とは異なり、(基本認証の最大の弱点である) 明文中にパスワードを送る事無く認証する事ができる。 他の多くの認証プロトコルのように、最大の危険の源は、通常中心のプロトコルそれ自体ではなく、その使用に関わるポリシー及びその手続き上で発見される。

目次

1 アクセス認証
1.1 HTTP/1.1 仕様書への依存
1.2 アクセス認証の枠組
2 基本認証スキーム
3 ダイジェストアクセス認証スキーム
3.1 導入
3.1.1 目的
3.1.2 全体の動作
3.1.3 ダイジェスト値の表現
3.1.4 限界
3.2 ダイジェストヘッダの仕様
3.2.1 WWW-Authenticate レスポンスヘッダ
3.2.2 Authorization リクエストヘッダ
3.2.3 Authentication-Info ヘッダ
3.3 Digest の動作
3.4 セキュリティプロトコルに関する折衝
3.5
3.6 Proxy-Authentication と Proxy-Authorization
4 セキュリティについての考察
4.1 基本認証を使用したクライアントの認証
4.2 ダイジェスト認証を使用したクライアントの認証
4.3 nonce 値の使用制限
4.4 ダイジェスト認証と基本認証との比較
4.5 繰り返し攻撃
4.6 複数の認証スキームによって生み出される弱点
4.7 オンライン辞書攻撃
4.8 中継者{Man in the Middle}
4.9 選択平文攻撃{Chosen plaintext attacks}
4.10 Precomputed dictionary attacks
4.11 Batch brute force attacks
4.12 偽造サーバによるなりすまし
4.13 パスワードを保存する事
4.14 要約
5 サンプルコード
6 謝辞
7 参照文献
8 筆者のアドレス
9 著作権表示全文

1 アクセス認証

1.1 HTTP/1.1 仕様書への依存

この仕様書は HTTP/1.1 仕様書 [2] と対を成すものである。 この仕様書では、先の文書 section 2.1 の拡張 BNF を使用し、更に先の文書中にて定義される仲介者{non-terminals} や、その他の状況も HTTP/1.1 仕様書に依存する。

1.2 アクセス認証の枠組

HTTP は、サーバがクライアントにリクエストを誰何{challenge} するため、あるいはクライアントが認証情報を提供するために使用する事ができる単純な誰何-応答{challenge-response} 認証メカニズムを提供する。 これは、認証スキームを識別するための拡張可能である大文字・小文字を区別しないトークンと、スキームを通じて認証を行うために必要なパラメータを含むコンマで区切られた属性-値という対のリストを使用する。

 auth-scheme    = token
 auth-param     = token "=" ( token | quoted-string )

401 (Unauthorized) レスポンスメッセージは、オリジンサーバがユーザエージェントの認証を誰何するために使用される。 このレスポンスは、リクエストされるリソースに効力がある最低一つの challenge を含む WWW-Authenticate ヘッダフィールドを含まなければならない。 407 (ProxyAuthentication Required) レスポンスメッセージは、プロクシがユーザエージェントの認証を誰何するために使用され、リクエストされるプロクシに効力がある最低一つの challenge を含む Proxy-Authenticate ヘッダフィールドを含まなければならない

 challenge   = auth-scheme 1*SP 1#auth-param

注意: ユーザエージェントは、複数の challenge や WWW-Authenticate ヘッダフィールドが供給される場合、challenge の内容はそれ自身にコンマで区切られた認証パラメータのリストが含んでいるかもしれないので、WWW-Authenticate や Proxy-Authenticate 各ヘッダフィールドを解析するのには特別な注意を払う必要があるであろう。

認証パラメータ realm は全ての認証スキーム中で定義される:

 realm       = "realm" "=" realm-value
 realm-value = quoted-string

(大文字・小文字を区別しない) realm 指示子は、challenge を発行する全ての認証スキームに対して要求される。 (大文字・小文字を区別する) realm 値は、アクセスされているサーバの正当なルート URL (その abs_path が空であるようなサーバにおける absoluteURI; [2] の section 5.2 参照) と連動して、保護された空間を定義する。 これらの realm によって、サーバ上の保護されたリソースは、自身の認証スキームや認証データベースを、それぞれ保護された領域のセットに分割できるようになる。 realm 値は、一般にオリジンサーバによって割り当てられた文字列であり、認証スキームに特有な追加的意味論{semantics} を持つであろう。 同じ auth-scheme であっても異なるrealm を持つ複数の challenge があるかもしれない事に注意。

オリジンサーバへ自身の認証を望むユーザエージェントは -- 必要ではないが、通常は 401 (Unauthorized) を受信した後 -- リクエストに Authorization ヘッダフィールドを含める事でそれを行う事ができる。 プロクシへ自身の認証を望むクライアントは -- 必要ではないが、通常は 407 (Proxy Authentication Required) を受信した後 -- リクエストに Proxy-Authorization ヘッダフィールドを含める事でそれを行う事ができる。 Authorization や Proxy-Authorization 各フィールド値は、リクエストされているリソースの realm についてのクライアントの認証情報を含む証明書から成る。 ユーザエージェントは、それを理解する最も強い auth-scheme の challenge の一つを使用する事を選択し、その challenge に基づくユーザからの証明書を要求しなければならない

 credentials = auth-scheme #auth-param

多くのブラウザは基本認証のみを認識し、それが提示される最初の auth-scheme である事を要求するであろう事に注意。 サーバは、基本認証のみが受理可能である場合、それのみを含むべきである。

保護される空間は、証明書が自動的に適用されうるドメインを決定する。 もしその前のリクエストが認証されたならば、その証明書は、認証スキーム、パラメータ、及びユーザ設定によって決定された期間、その保護空間内の他の全てのリクエストにおいて再使用する事ができる。 認証スキームによって他のものが定義されなかった場合、単一の保護空間はそのサーバの範囲の外に拡張する事はできない。

オリジンサーバがリクエストと共に送られた証明書を受け入れたくない場合は、401 (Unauthorized) レスポンスを返すべきである。 そのレスポンスは、リクエストされたリソースに適用できる最低一つの (できれば最新の)challenge を持った WWW-Authenticate ヘッダフィールドを含めなければならない。 プロクシがリクエストと共に送られた証明書を受け入れたくない場合は、407 (Proxy Authentication Required) レスポンスを返すべきである。 そのレスポンスは、リクエストされたリソースのためのプロクシに適用できる最低一つの (できれば最新の) challenge を持った WWW-Authenticate ヘッダフィールドを含めなければならない

HTTP プロトコルでは、アクセス認証についてアプリケーションをこの単純な誰何-応答メカニズムに制限しない。 転送レベルにおける暗号化やメッセージのカプセル化、あるいは認証情報を詳述する追加的ヘッダフィールドを伴うような、追加的なメカニズムを使用する事ができる。 しかし、これらの追加的メカニズムはこの仕様書では定義されない。

プロクシは、オリジンサーバによるユーザエージェント認証に関しては完全に透過なものでなければならない。 すなわち、WWW-Authenticate とAuhtorization ヘッダには手を付けずに転送し、[2] の section 14.8 にて見られる規定に従わなければならない。 Proxy-Authenticate と Proxy-Authorization 両ヘッダフィールドは、ホップバイホップヘッダである ([2]の section 13.1 参照)。

2 基本認証スキーム

"基本{Basic}" 認証スキームは、クライアントは各々の realm についてユーザ ID とパスワードをもって自身を認証しなければならないというモデルに基づいている。 realm 値は、そのサーバ上の他の realm との同等性の比較のみのために存在しうる、それ自体は読んでも意味のわからない{opaque} 文字列であるとみなされるべきである。 サーバは、その保護された Request-URI の空間のためのユーザ ID とパスワードが正しいと証明できる場合にのみ、リクエストを処理するであろう。 オプショナルな認証パラメータは存在しない。

基本認証について、上記の枠組は以下のように利用される:

 challenge   = "Basic" realm
 credentials = "Basic" basic-credentials

保護空間内の URI について認証されていないリクエストを受信した上で、オリジンサーバは以下のような challenge を返す事ができる

 WWW-Authenticate: Basic realm="WallyWorld"

ここで "WallyWorld" は、Request-URI の保護空間を識別するためにサーバによって割り当てられた文字列である。 プロクシは、Proxy-Authenticate ヘッダフィールドを使って同じ challenge を返す事ができる。

認証を受信するために、クライアントは証明書中にて base64 [7] エンコードされた、単一のコロン (":") 文字にて区切られた userid と password の文字列を送信する。

 basic-credentials = base64-user-pass
 base64-user-pass  = <76 文字/行とは制限されていない、
                  user-pass の base64 [4] エンコーディング>
 user-pass   = userid ":" password
 userid      = *<":" を含まない TEXT>
 password    = *TEXT

userid は大文字・小文字を区別するであろう。

ユーザエージェントが userid "Aladdin" と password "open sesame" を送信する場合、以下のヘッダフィールドを使用するであろう:

 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

クライアントは、その Request-URI の path 領域中の最後の記号要素の深さと同等、あるいはそれより深い所 (訳注: 要するに現在のディレクトリ以下) は、現在の challenge の Basic realm 値によって指定される保護空間内にあるという事を仮定すべきである。 クライアントは、その空間にあるリソースへのリクエストに対応する Authorization ヘッダを、サーバから別の challenge を受信する事無く、先んじて送信してもよい。 同様に、クライアントがプロクシにリクエストを送る場合、プロクシサーバから別の challenge を受信する事無く Proxy-Authorization ヘッダフィールド中の userid と password を再使用してもよい。 基本認証に関連したセキュリティについての考察は section 4 を参照。

3 ダイジェストアクセス認証スキーム

3.1 導入

3.1.1 目的

"HTTP/1.0" として参照されるプロトコルには、基本アクセス認証スキーム [1] の仕様が含まれている。 このスキームは、ユーザー名とパスワードが暗号化されていない状態でネットワーク上を渡されるので、ユーザー認証としてセキュアな方法であると考えられてはいない。 この節では、"ダイジェストアクセス認証" と呼ばれる、明文中にパスワードを送信しないスキームのための仕様を示す。

ダイジェストアクセス認証スキームは、World Wide Web 内のセキュリティのために必要なものとしての完全なる解答である事を意図するものではない。 このスキームでは、メッセージ内容の暗号化は提供しない。 その意図とは、単に基本認証の最も重大な欠点を回避するようなアクセス認証方法を作成するという事である。

3.1.2 全体の動作

基本アクセス認証のように、ダイジェストスキームは単純な誰何-応答パラダイムに基づくものである。 ダイジェストスキームは、nonce 値を使用して誰何する。 妥当{valid} なレスポンスは、ユーザ名、パスワード、与えられる nonce 値、HTTP メソッド、リクエストされる URI のチェックサム (既定では、MD5 チェックサム) を含んでいる。 この方法では、パスワードが明文中に送信される事は無い。 ちょうど基本スキームを用いる時のように、ユーザ名とパスワードはこの文書によって述べられてない幾つかのやり方によって前持って取り決められているはずである。

3.1.3 ダイジェスト値の表現

オプショナルなヘッダによって、サーバはチェックサムあるいはダイジェストを作成するために使用されるアルゴリズムを指定できる。 既定では MD5 アルゴリズムが使用され、それのみがこの文書中に記述される唯一のアルゴリズムである。

この文書の目的において、128 ビットの MD5 ダイジェストは 32 の ASCII印刷可能な文字として表現される。 128 ビットダイジェスト中のビットは、以下の様に ASCII 表現を一度に 4 ビットづつ、最も重要なビットから最もそうでないビットへと置換される。 各 4 ビットは、文字 0123456789abcdefからのよく知られている 16 進記法によって表現される。 つまり、2 進法の0000 は文字 '0'、0001 は '1' として、'f' としての 1111 まで順に表現される。

3.1.4 限界

この文書に記述されるダイジェスト認証スキームは、多くの既知の限界に苦しめられる。 これは基本認証の置換を意図したものであり、またそれ以外の何物でもない。 これはパスワードに基づいたシステムであり、(サーバ側での) 全てのあらゆるパスワードシステムと同じ問題に苦しめられる。 特に、ユーザとサーバとの間でユーザのパスワードを確立するための初期のセキュアなる取り決めに対する対策は、このプロトコルの中で示されない。

ユーザと実装者は、このプロトコルが Kerberos 程にも、また client-side 秘密鍵スキーム程にもセキュアではないという事に気づいているべきである。 それであっても、このプロトコルは、何もしないよりも、また一般に telnet や ftp と共に使用されるものよりも、また基本認証よりは良い。

3.2 ダイジェストヘッダの仕様

ダイジェストアクセス認証スキームは、概念的に基本スキームに似ている。 WWW-Authenticate 及び Authorization 各ヘッダラインの修正されたフォーマットは、以下に詳述される。 更に、新しいヘッダである、Authentication-Info が詳述される。

3.2.1 WWW-Authenticate レスポンスヘッダ

サーバは、アクセス制限されたオブジェクトへのリクエストを受信したが、受け入れ可能な Authorization ヘッダが送られない場合、"401 Unauthorized" ステータスコードと、上に定義される枠組毎に WWW-Authenticate ヘッダを返すが、この時ダイジェストスキームは以下の様に利用される:

 challenge        =  "Digest" digest-challenge

 digest-challenge  = 1#( realm | [ domain ] | nonce |
                     [ opaque ] |[ stale ] | [ algorithm ] |
                     [ qop-options ] | [auth-param] )

 domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
 URI               = absoluteURI | abs_path
 nonce             = "nonce" "=" nonce-value
 nonce-value       = quoted-string
 opaque            = "opaque" "=" quoted-string
 stale             = "stale" "=" ( "true" | "false" )
 algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
 qop-options       = "qop" "=" <"> 1#qop-value <">
 qop-value         = "auth" | "auth-int" | token

上記に使用される指示子の値の意味は以下の通りである:

realm

ユーザが使用するユーザ名とパスワードを知るためにユーザに表示されるための文字列。 この文字列には少なくとも認証を実行するホストの名前を含むべきであり、またアクセスを許可するユーザを追加的に列挙する事ができる。 それは例えば、"registered_users@gotham.news.com" のようになるであろう。

domain

RFC XURI [7] にて詳述されるような、保護空間を定義する、ダブルクォーテーションにて括られ、更にスペースにて分割された URI のリスト。 URIが abs_path の場合、アクセスされているサーバの正当なルート URL (上記 section 1.2 参照) に相対する。 このリスト中の absoluteURI には、アクセスされているサーバとは異なるものを指すものがあるかもしれない。 クライアントは、同じ認証情報を送る事ができる URI のセットを決定するためにこのリストを使用する事ができる: すなわち、接頭辞としてこのリスト内に URI がある任意の URI は (それぞれが絶対 URI とされた後に) 同じ保護空間内にあると仮定する事ができる。 この指示子が省略されるか、あるいは空だった場合、クライアントは、応答するサーバの全てのURI が保護空間となる仮定すべきである。 この指示子は Proxy-Authenticate ヘッダ中では、その保護空間が常にプロクシ全体であるために、意味を持たない; 現在では、それは無視されるべきである。

nonce

401 レスポンスが返される毎に一意に生成されるべきサーバによって指定されたデータ文字列。 この文字列は base64 あるいは 16 進データである事が推奨される。 特に、この文字列はクォート文字列としてヘッダライン中にて渡されるので、ダブルクォート文字の使用は許されない。

nonce の内容は実装依存である。 その実装の品質は良い選択に依存する。 例えば、nonce は以下の base 64 エンコーディングから成るものとする事ができる

 time-stamp H(time-stamp ":" ETag ":" private-key)

ここで time-stamp はサーバによって生成された時刻あるいはその他の繰り返さない値、ETag はリクエストエンティティに関連した HTTP ETag ヘッダの値、private-key はサーバのみが知るデータである。 この形式のnonce において、サーバはクライアント認証ヘッダを受信後にハッシュ部分を再計算し、もしそれがそのヘッダからの nonce と一致しないか、あるいはその time-stamp 値が十分に最近のものでない場合、そのリクエストを拒否する。 このようにして、サーバは nonce の有効な時間を制限する事ができる。 ETag を含む事によって、リソースの更新バージョンを繰り返しリクエストする事を防ぐ。 (注意: nonce にクライアントの IP アドレスを含む事で、元々それを持つ同じクライアントへ nonce の再使用を制限する能力をサーバに提供するように見えるであろう。 しかし、それはプロクシを複数持ち、単一ユーザからのリクエストはしばしばそのプロクシ中の異なるプロクシを通り抜けてくるような環境においては破綻する。 更に、それが IP アドレススプーフィングを困難にするわけでもない。)

実装は、繰り返し攻撃から防御するために、以前使用した nonce やダイジェストを受け入れないとする事ができる。 あるいは、実装は、POST や PUT リクエストのために一回きりのnonce やダイジェストを使う、また GET リクエストのために一回きりのtime-stamp を使うとする事ができる。 そこに含まれる問題についての詳細についてはこの文書の section 4 を見よ。

nonce は、クライアントにとってそれ自体は読んでも意味のわからない{opaque} ものである。

opaque

サーバによって指定されるデータの文字列であり、同じ保護空間中の URIへの以降のリクエストの Authorization ヘッダに変更せずにクライアントによって返されるべきものである。 この文字列は base64 あるいは 16進データである事が推奨される。

stale

nonce 値が新鮮でなくなった事により、クライアントからのそれ以前のリクエストが拒否されたという事を示すフラグ。 stale が TRUE (大文字小文字を区別しない) の場合、クライアントはユーザへ新しいユーザ名とパスワードを再入力させる事なく、新しい暗号化されたレスポンスへのリクエストを単に再試行したいと思うかもしれない。 もし nonce は無効だがそのnonce についてのダイジェストが妥当であるようなリクエストを受け取った場合 (これはクライアントが正しいユーザ名/パスワードを知っている事を表す)、サーバは単に stale を TRUE にセットすべきである。 stale がFALSE, TRUE 以外の文字列、あるいは stale 指示子が存在しなかった場合は、そのユーザ名及びパスワードは無効であり、新しい値が得られるに違いない。

algorithm

ダイジェストとチェックサムを作り出すために使われる一対のアルゴリズムを示す文字列。 これがない場合は、"MD5" であるとみなされる。 そのアルゴリズムを理解できない場合、その誰何は無視されるべきである (それが複数ある場合は、それとは異なるものが使用される)。

この文書中では、秘密の "secret" を持つデータ "data" にダイジェストアルゴリズムを適用する事によって得られる文字列は KD(secret, data)として、またデータ "data" にチェックサムアルゴリズムを適用する事によって得られる文字列は H(data) として表されるであろう。 記法 unq(X)は、そのまわりに引用符のない quoted-string X の値を意味する。

"MD5" 及び "MD5-sess" アルゴリズムにおいて

 H(data) = MD5(data)

また

 KD(secret, data) = H(concat(secret, ":", data))

例えば、ダイジェストは secret とコロンと data を連結したものの MD5 である。 "MD5-sess" アルゴリズムはサードパーティの認証サーバを効率的にする狙いを持つ; その使用法による違いについては、section 3.2.2.2 にて詳述。

qop-options

この指示子はオプショナルであるが、RFC 2069 [6] との後方互換性のためにのみ作成された; 従って、このバージョンのダイジェストスキームを満足する全ての実装はこれを使用すべきである。 これが使用されている場合、それはサーバによって提供される "保護の品質" の値を表す一つ以上の token の引用符で括られた文字列である。 "auth" という値は認証{authentication} を表す; また "auth-int" は完全に保護された認証{authentication with integrity protection} を表す; これを使うアプリケーションのためのレスポンス指示子値の計算については以下の記述を見よ。 認識できないオプションは無視されなければならない

auth-param

この指示子は将来の拡張において使用される。 あらゆる認識できない指示子は無視されなければならない

3.2.2 Authorization リクエストヘッダ

クライアントは Authorization ヘッダラインを転送する時に、リクエストを再試行するはずである。 それは上記の枠組によって定義され、以下のように利用される。

 credentials      = "Digest" digest-response
 digest-response  = 1#( username | realm | nonce | digest-uri
                 | response | [ algorithm ] | [cnonce] |
                 [opaque] | [message-qop] |
                     [nonce-count]  | [auth-param] )

 username         = "username" "=" username-value
 username-value   = quoted-string
 digest-uri       = "uri" "=" digest-uri-value
 digest-uri-value = request-uri   ; HTTP/1.1 にて記述
 message-qop      = "qop" "=" qop-value
 cnonce           = "cnonce" "=" cnonce-value
 cnonce-value     = nonce-value
 nonce-count      = "nc" "=" nc-value
 nc-value         = 8LHEX
 response         = "response" "=" request-digest
 request-digest = <"> 32LHEX <">
 LHEX             =  "0" | "1" | "2" | "3" |
                     "4" | "5" | "6" | "7" |
                     "8" | "9" | "a" | "b" |
                     "c" | "d" | "e" | "f"

opaque, algorithm 各フィールド値はリクエストされているエンティティにおける WWW-Authenticate レスポンスヘッダにて提供されるものでなければならない。

response

以下に定義される方法で計算される 32 字の 16 進文字列。 これによってそのユーザがパスワードを知っている事を証明する。

username

指定された realm 中のユーザ名。

digest-uri

Request-Line の Request-URI にある URI; プロクシは転送において Request-Line を変更できるので、ここにコピーしておくのである。

qop

クライアントがメッセージにどんな "保護の品質" を提供したかを表すもの。 これがある場合、その値は WWW-Authenticate ヘッダ中のサーバがそれをサポートするという事を示す選択肢の内の一つでなければならない。 これらの値は request-digest の計算に影響する。 これは単一の token であり、WWW-Authenticate 中にあるような引用符で括られた選択肢のリストではないという事に注意せよ。 この指示子は RFC 2069 [6] の最低限実装への後方互換性をもたせるためにオプショナルであるが、qop が WWW-Authenticate ヘッダフィールド中に qop 指示子を提供する事によってサポートされるという事をサーバが示すならば、これが使われるべきである

cnonce

サーバが WWW-Authenticate ヘッダフィールドにおいて qop 指示子 (上記参照) を送ってきた場合は必ず指定されなければならない が、送られなかった場合は指定されてはならない 。 cnonce-value はクライアントによって提供され、選択的平文攻撃を避けるため、相互の認証を提供するため、あるメッセージの完全なる保護{integrity protection} を提供するために、クライアントとサーバの両方に使用される、それ自体は読んでも意味のわからない{opaque} 引用符で括られた文字列値である。 response-digest と request-digest 値の計算については以下の記述を見よ。

nonce-count

サーバが WWW-Authenticate ヘッダフィールドにおいて qop 指示子 (上記参照) を送ってきた場合は必ず指定されなければならない が、送られなかった場合は指定されてはならない 。 nc-value はこのリクエストでの nonce 値をクライアントが送った (現在のリクエストを含む) リクエスト数の 16 進記述での数である。 例えば、nonce 値が与えられるレスポンスに対して送る最初のリクエストでは、クライアントは "nc=00000001" を送る。 この指示子の目的は、この数のコピーを保持する事で、サーバがリクエストの繰り返しを発見できるようにする事である。 つまり、もし同じ nc-value が二度見られたならば、そのリクエストは繰り返されたものである。 request-digest 値の構文については以下の記述を見よ。

auth-param

この指示子は将来の拡張にて使用される。 あらゆる認識できない指示子は無視されなければならない

もし指示子やその値が適切でなかったり、要求される指示子が得られない場合、適切なレスポンスは 400 Bad Request である。 単一クライアントから繰り返されるログインの失敗はパスワードを推測しようとしている攻撃者を示しているかもしれないので、request-digest が正しくない場合は記録されるべきである。

上記の request-digest の定義はその値のエンコーディングを示す。 以下の定義はその値がどのように計算されるかを示すものである。

3.2.2.1 Request-Digest

"qop" が "auth" か "auth-int" である場合:

 request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
                                     ":" nc-value
                                     ":" unq(cnonce-value)
                                     ":" unq(qop-value)
                                     ":" H(A2)
                             ) <">

"qop" 指示子が与えられない場合 (RFC 2069 との互換性のための構文)

    request-digest  =
               <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">

A1 と A2 についての定義は以下を見よ。

3.2.2.2 A1

"algorithm" 指示子の値が "MD5" か指定されない場合、A1 は:

 A1       = unq(username-value) ":" unq(realm-value) ":" passwd

ここで

 passwd   = < ユーザのパスワード >

"algorithm" 指示子の値が "MD5-sess" の場合、A1 はサーバからの WWW-Authenticate challenge を受信するクライアントによって最初のリクエスト時に一度だけ計算される。 それにはその challenge からのサーバ nonce、及び最初の以下のような構文の A1 クライアント nonce 値を使う:

 A1       = H( unq(username-value) ":" unq(realm-value)
                ":" passwd )
                ":" unq(nonce-value) ":" unq(cnonce-value)

これはそれぞれの "認証セッション" とは異なるような以降のリクエストやレスポンスの認証のための 'セッションキー' を生成する事によって、任意の一つの鍵から作られるハッシュの量を制限する。 (注意: 認証セッションの更なる議論については section 3.3 を見よ。 ) サーバは A1 値を生成するためにのみユーザの credentials のハッシュを使う必要があるので、web サーバは実際のパスワード値は必要としないために、この構文はサードパーティによる認証サービスと合わせて使われるであろう。 そのようなプロトコルの使用についてはこの仕様書の範囲を越える。

3.2.2.3 A2

"qop" 指示子の値が "auth" か指定されない場合、A2 は:

 A2       = Method ":" digest-uri-value

"qop" の値が "auth-int" ならば、A2 は:

 A2       = Method ":" digest-uri-value ":" H(entity-body)
3.2.2.4 指示子の値と quoted-string

多くの指示子の値は、"username-value" のように、"quoted-string" として定義されている事に注意せよ。 しかし、"unq" 表記は括られる引用符号が文字列 A1 を形成するために取り除かれている事を示している。 従ってもしAuthorization ヘッダが以下のフィールドを含んでおり

 username="Mufasa", realm=myhost@testrealm.com

またユーザ Mufasa のパスワードが "Circle Of Life" の場合、H(A1) はH(Mufasa:myhost@testrealm.com:Circle Of Life) とダイジェスト文字列中では引用符が無いであろう。

そのスペースがクオートされた文字列中、あるいはダイジェスト文字列を構成するエンティティボディに存在する以外は、ダイジェスト関数 H() への文字列としてスペースは許されない。 例えば、文字列上で例示した A1 の場合

 Mufasa:myhost@testrealm.com:Circle Of Life

コロンの両隣にはスペースは付加されないが、パスワード値の中に使われる単語の間のスペースは有効である。 同様に、H() によってダイジェスト化された文字列も、そのスペースがクオートされた文字列中、あるいはダイジェスト化されたエンティティボディにある以外は、それらのフィールドのデリミタであるコロンの両隣にスペースを付加してはならない。

また、完全なる保護が適用される場合 (qop=auth-int)、H(entity-body) はエンティティボディのハッシュであり、メッセージボディのハッシュでは無いという事にも注意せよ - すなわち、任意の転送エンコーディングが送信者によって施される前に、また受信者によってそれが取り除かれた後にそのハッシュは計算される。 任意の multipart content-type のそれぞれの部分には multipart 境界及び埋め込まれたヘッダが含まれている事に注意せよ。

3.2.2.5 その他について考察

"Method" 値は [2] の section 5.1 にて詳述されるような HTTP リクエストメソッドである。 "request-uri" は [2] の section 5.2 にて詳述されるようなリクエストラインに含まれる Request-URI である。 これは [2] のsection 5.2 にて詳述されるように "*", "absoluteURL", "abs_path" のいずれかであるだろうが、それらは Request-URI と一致していなければならない。 特に、Request-URI が "absoluteURL" であれば、"request-uri"も "absoluteURL" でなければならない。 "cnonce-value" は選択的平文攻撃に対抗するためのクライアントが選択するオプショナルな値である。

認証をするサーバは、"uri" 指示子によって指定されるリソースは Request-Line にて指定されるリソースと同じであるとみなさなければならない; そうでなければ、サーバは 400 Bad Request エラーを返すべきである。 (これは攻撃の徴候であるかもしれないので、サーバ実装者はそのようなエラーを記録したいと思うかもしれない。) このフィールド中のリクエスト URL から情報をコピーする目的は、中間プロクシがクライアントの Request-Line を変えるかもしれない可能性を扱うためである。 この変更された (しかしおそらく意味的に同等な) リクエストにおいて、そのクライアントによって計算されるダイジェストは同じにはならないであろう。

実装者は認証された通信で共有キャッシュがどのように動作するかを知っているべきである。 HTTP/1.1 プロトコルでは、共有キャッシュ ([2] のsection 13.7 参照) は Authorization ヘッダを含むリクエストを受信した時、そのリクエストに対するレスポンスを返すが、以下の二つの Cache-Control 指示子 ([2] の section 14.9 参照) の一つがレスポンス中になければ、その他の任意のリクエストへの返答としてそのレスポンスを返してはならない と定義している。 元のレスポンスが "must-revalidate" Cache-Control 指示子を含んでいる場合、キャッシュは以降のリクエストへの応答としてそのレスポンスのエンティティを使う事ができる が、オリジンサーバが新しいリクエストを認証できるように新しいリクエストからのリクエストヘッダを使って、初めにオリジンサーバにそれを再検証しなければならない。 あるいは、元のレスポンスが "public" Cache-Control 指示子を含んでいる場合、以降のリクエストへの応答としてそのレスポンスエンティティを返す事ができる

3.2.3 Authentication-Info ヘッダ

Authentication-Info ヘッダはレスポンス中にて成功した認証についてのいくつかの情報を通信するためにサーバによって使用される。

 AuthenticationInfo = "Authentication-Info" ":" auth-info
 auth-info          = 1#(nextnonce | [ message-qop ]
                        | [ response-auth ] | [ cnonce ]
                        | [nonce-count] )
 nextnonce          = "nextnonce" "=" nonce-value
 response-auth      = "rspauth" "=" response-digest
 response-digest    = <"> *LHEX <">

nextnonce 指示子の値は、クライアントが将来の認証レスポンスのために使用する事をサーバが望む nonce である。 サーバは、一度きりの、あるいはその他の方法で変更したような nonce を実行するという意味である nextfield 値を持つ Authentication-Info ヘッダを送るであろう。 nextnonce フィールドが存在する場合、クライアントはその次のリクエストに Authorization ヘッダを含む時にその値を使用すべきである。 クライアントがこれに失敗した場合、"stale=TRUE" となるサーバから再認証するようなリクエストを発行する事になるであろう。

サーバ実装はこのメカニズムの使用が暗に意味するパフォーマンスを十分考慮すべきである; すなわち、もし全てのレスポンスが、サーバに予約されている次のリクエストに使用されなければならないような nextnonce 指示子を含んでいるような場合は、パイプラインされたリクエストは可能ではないだろう。 リクエストパイプラインを許可するために制限時間内で古い nonce 値が許される場合、パフォーマンスとセキュリティのトレードオフが考慮されるべきである。 nonce-count の使用によって、パイプラインの有害な影響を受ける事無く新しいサーバの nonceはほとんどのセキュリティ上の利点を維持できる。

message-qop

サーバによるレスポンスに適用される "保護の品質" オプションを表すもの。 "auth" という値は認証を表す; また "auth-int" は完全に保護された認証を表す。 サーバは、これに対応するリクエスト中にてクライアントから送られた message-qop 指示子と同じ値をレスポンス中にて使用すべきである

"response-auth" 指示子中のオプショナルなレスポンスダイジェストは相互認証をサポートする -- サーバは、それがユーザの secret を知っている時はそれを証明し、また qop=auth-int がある場合は制限されたレスポンスの完全なる保護を提供する。 "response-digest" 値は通常 Authorization ヘッダ中の "request-digest" を基づいてもって計算されるが、リクエスト中のAuthorization ヘッダにて指定されない場合は、A2 は

 A2       = ":" digest-uri-value

また "qop=auth-int" がある場合は、A2 は

 A2       = ":" digest-uri-value ":" H(entity-body)

ここで "digest-uri-value" は、リクエスト中の Authorization ヘッダにおける "uri" 指示子の値である。 "cnonce-value" と "nc-value" は、このメッセージがレスポンスにあるようなクライアントリクエストの一つでなければならない。 "qop=auth" か "qop=auth-int" が指定された場合は、"response-auth", "cnonce", "nonce-count" のいずれかの指示子が存在しなければならない

Authentication-Info ヘッダは、チャンク形式転送コーディングを通じて転送される HTTP メッセージの trailer 中にも許される。

3.3 Digest の動作

Authorization ヘッダの受信において、サーバは提出されたユーザ名に対応するパスワードを調べる事により、その有効性をチェックできる。 その後、サーバはクライアントによって実行された同じダイジェスト動作 (例えばMD5) を実行し、またその結果を与えられた request-digest と比較しなければならない。

HTTP サーバは実際にユーザの平文パスワードを知る必要がないという事に注意せよ。 H(A1) がサーバにとって利用可能であれば、Authorization ヘッダの有効性が検証されるかもしれない。

保護空間における WWW-Authenticate 誰何に応答するクライアントは、保護空間との認証セッションを開始する。 認証セッションは、クライアントが保護空間内の任意のサーバから他の WWW-Authenticate 誰何を受信するまで続く。 クライアントは、保護空間への将来のリクエストで Authorization ヘッダを構成するために使用する認証セッションに関連する username, password, nonce, nonce count, opaque 各値を記憶しておくべきである。 Authorization ヘッダは先取りして含まれているかもしれない; そうする事によって、サーバの効率を改善し、認証誰何に対する余分なラウンドトリップを避ける。 サーバは、含まれる nonce 値が新鮮でなかったとしても、古いAuthorization ヘッダ情報を受け入れる事を選択する事ができる。 または、サーバは、クライアントにリクエストを再試行させるために、新しい nonce 値と共に 401 レスポンスを返す事ができる; このレスポンスに state=TRUE を指定する事によって、サーバは新しい username と password を表示する事無く、クライアントに新しい nonce 値で再試行するように伝える。

クライアントはセッションの間中サーバによって与えられる opaque 指示子の値を返す必要があるので、opaque データは認証セッションの状態情報を伝えるのに使用されるかもしれない。 (またそのような使われ方は nonce 中に状態を含む事によってより簡単にまた安全に成し遂げる事ができるという事にも注意せよ。) 例えば、サーバは実際は他のサーバにあるような認証の内容を応答する事ができる。 これは、最初の 401 レスポンスに次のサーバ上のURI を含んだ値の domain 指示子を、また状態情報を含んだ opaque 指示子を含めさせる事によって成し遂げる事ができる。 クライアントは、サーバが 301/302 レスポンスを応答する時に、次のサーバ上の URI を指し示すリクエストを再試行するであろう。 クライアントはリダイレクションに従い、また <opaque> データを含んだ、Authorization ヘッダを渡すであろう。

基本認証スキームと同じく、プロクシはダイジェストアクセス認証スキームにおいても完全に透過である。 つまり、プロクシは WWW-Authenticate, Authentication-Info, Authorization 各ヘッダを加工せずに転送しなければならない。 プロクシがリクエストをサーバに転送する前にクライアントを認証したい場合、以下の section 3.6 にて記述される Proxy-Authenticate,Proxy-Authorization 各ヘッダを使用して行う事ができる。

3.4 セキュリティプロトコルに関する折衝

クライアントがどのようなセキュリティスキームを扱う事ができるのかをサーバが知る事ができるのは有用である。

サーバが、その認証方法としてダイジェストスキームを求める事は、例えクライアントがそれをサポートしているという事をサーバが知らなくても、可能である。 クライアントは、もし扱えない認証スキームのみをサーバが指定した場合、緩やかなダウングレードを行うべきである。

3.5 例

以下の例では、次の事を仮定する。 アクセス制限された文書は GET リクエストでそのサーバへと応答されている。 文書の URI は "http://www.nowhere.org/dir/index.html" である。 クライアントとサーバの両方が、この文書の username が "Mufasa" であり、また password が "Circle Of Life" (各語の間にそれぞれスペースが入る) である事を知っている。

始めにクライアントがその文書にリクエストをする時、Authorization ヘッダが送られないと、サーバは以下の様に応答する:

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

クライアントはユーザに username と password の入力を促させ、その後以下のような Authorization ヘッダを含む、新しいリクエストをもって応答するであろう。

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

3.6 Proxy-Authentication と Proxy-Authorization

ダイジェスト認証スキームは、Proxy-Authenticate と Proxy-Authorizationヘッダの使用によって、ユーザとプロクシ、プロクシとプロクシ、プロクシとオリジンサーバの認証にも使用する事ができる。 これらのヘッダはHTTP/1.1 仕様書 [2] の section 10.33 と 10.34 にて記述される Proxy-Authenticate と Proxy-Authorization ヘッダの事例であり、これらの振る舞いはそこに記述される制限に従う。 プロクシ認証との相互動作は既に記述されたものとよく似ている。 認証を要求するようなリクエストの受信において、プロクシやサーバは "Proxy-Authenticate" ヘッダを含む "407 ProxyAuthentication Required" レスポンスを発行しなければならない。 Proxy-Authenticate ヘッダ中に使用される digest-challenge は、上記 section3.2.1 の WWW-Authenticate ヘッダのものと同じである。

(※訳注) section 10.33, 10.34 は、section 14.33, 14.34 の typo 。

クライアントやプロクシは、上記 section 3.2 の Authorization ヘッダに記述される指示子を持った、Proxy-Authorization ヘッダを含むリクエストを再発行しなければならない。

以降のレスポンスでは、サーバはその Authentication-Info ヘッダフィールドのものと同じ指示子を含む Proxy-Authentication-Info を送る。

原則的にクライアントは自身をプロクシやエンドサーバとに認証させる事はできるが、同じレスポンスではないという事に注意せよ。

4 セキュリティについての考察

4.1 基本認証を使用したクライアントの認証

基本認証スキームは、転送媒体として物理層ネットワークを通じて平文を転送するので、セキュアなユーザ認証方法でも、エンティティを保護する方法でもない。 HTTP は、基本認証にセキュリティや (ワンタイムパスワードを使うスキームのような) その価値を増すために使用される追加的認証スキームや暗号化メカニズムを禁止していない。

基本認証の最も重大な欠点は、物理層ネットワークを実質的に平文であるユーザのパスワードを転送してしまっている事である。 これはダイジェスト認証が取り組んでいる問題である。

基本認証は必然的に平文であるパスワードの転送を含んでいるので、個人情報や価値のある情報を保護するためには (追加的セキュリティ無しで) 使用されるべきではない

基本認証は一般的に個人の識別目的に使用される -- 故に、例えばサーバ上の使用の正確な統計を取る目的で、個人識別のためにユーザはユーザ名とパスワードを提供する必要がある。 この目的で使用される時、保護された文書への不正なアクセスが大きな問題とはならないかどうか、その使用において危険は無いという事を考慮する必要がある。 これは、サーバがユーザ名とパスワードの両方を発行し、かつユーザに自身のパスワードを選択させない場合にのみ正しいものである。 セキュリティに注意を払わないユーザは、しばしば複数のパスワードを管理する手間を省くために単一のパスワードを再利用するので危険が生じる。

もしユーザが自身のパスワードを選択する事をサーバが許可した場合、サーバ上の文書への認証されないアクセスだけでなく、ユーザが同じパスワードで保護する他のシステム上の他のリソースへの認証されないアクセスも脅威となる。 更に、サーバのパスワードデータベースでは、そのパスワードの多くはユーザの他のサイトのためのパスワードであるかもしれない。 従って、この情報がセキュアな手法にて維持されなければ、そのようなシステムの所有者や管理者は、そのシステムの全てのユーザをそれら全てのサイトへの認証されないアクセスという危険に晒す事になるだろう。

また基本認証は偽造サーバによるなりすましに脆弱である。 もしユーザが基本認証によって保護される情報を含むホストに接続していると信じさせ、実際には、悪意を持つサーバやゲートウェイに接続させる事ができれば、攻撃者はパスワードを要求し、後の使用のためにそれを保存し、エラーを装う事ができる。 この種の攻撃はダイジェスト認証では実現できない。 サーバ実装者は、ゲートウェイあるいは CGI スクリプトによるこの種の偽装の可能性に対して警戒すべきである。 特にサーバが単にゲートウェイに接続を渡す事は非常に危険である。 そのようなゲートウェイは、その後クライアントによって検知できないような方法でオリジンサーバのふりをしている間にクライアントとの複数のトランザクションにたずさわるために持続的接続メカニズムを使用する事ができる。

4.2 ダイジェスト認証を使用したクライアントの認証

ダイジェスト認証は、例えば、公開鍵暗号メカニズムと比較して、強い認証メカニズムを提供するものではない。 しかし、これは、例えば LDAP [10], POP, IMAP (RFC 2195 [9] 参照) での使用のために提案された CRAM-MD5 よりも十分に強い。 これは、ダイジェスト認証よりもはるかに弱く、さらに危険である基本認証メカニズムと置き換えられる事が意図されています。

ダイジェスト認証は、実際のパスワードを保護する事以上の機密性保護は提供されない。 他のリクエストやレスポンスは全て盗聴者{eavesdropper} にとっては取得可能である。

ダイジェスト認証は単にどちらか片道へのメッセージについての制限された完全なる保護を提供する。 qop=auth-int メカニズムが使用された場合、WWW-Authenticate と Authorization 各ヘッダフィールドのレスポンス指示子の値 (上記 section 3.2 参照) の計算の中で使用されるメッセージのその部分は保護される。 ほとんどのヘッダフィールドやそれらの値は、中継者による攻撃{man-in-the-middle attacks} の一つとして修正されるかもしれない。

ダイジェスト認証では、セキュアな HTTP 通信についての多くのニーズを満たす事はできない。 それらのニーズのためには、TLS あるいは SHTTP がより適切なプロトコルである。 特に、ダイジェスト認証は機密性保護を要求するようなあらゆる処理のためには使用する事はできない。 しかし、ダイジェスト認証の有用でまた適切である多くの機能は残っている。 実際にはすぐに、基本認証を使用しているような現在の使用されているあらゆるサービスはダイジェスト認証に切り替えられるべきである。

4.3 nonce 値の使用制限

ダイジェストスキームは、(上記 section 3.1 にて示されたように) request-digest 値の生成の種とするためにサーバに指定された nonce を使う。 section 3.1 中で例示される nonce にて示されるように、サーバは特定のクライアントから、特定のリソースへ、時間や、使用回数、あるいは他の制限方法による一定期間のみ使用できるような nonce を自由に構築する事できる。 そうする事によって、例えば、繰り返し攻撃 (section 4.5 参照) からの防御を強くする。 しかし、nonce の生成やチェックのために選択された方法が更にパフォーマンスやリソースの結果{implications} を持つ事は注目されるべきである。 例えば、サーバは、それぞれの最も新しく発行された nonce が返されたか否かの記録を保持する事や、すべてのレスポンスの Authentication-Info ヘッダフィールド中の next-nonce 指示子を送る事によって、それぞれの nonce 値が一度だけ使用される事ができるようにする事ができる。 これは即時の繰り返し攻撃からさえも防御するが、nonce 値をチェックするという高いコストがあり、また恐らくより重要な事は、(たぶん古い nonce 値を返す事による) 任意のパイプラインで送られたリクエストの認証失敗を引き起こす事になるだろう。 同様に、リソースに対する Etag 値のようなリクエストに特有な要素を組み込む事は、リソースのそのバージョンに nonce の使用を制限し、またパイプラインを破壊する。 故に、副作用を持つが、そうしない時に受け入れ難いパフォーマンスを行うような方法のためにそうする事は有用かもしれない。

4.4 ダイジェスト認証と基本認証との比較

ダイジェスト認証も基本認証も共に、セキュリティの強さの範囲では最も弱い部類にある。 しかし、二つの認証の比較は、基本認証をダイジェスト認証に置き換える有用性、むしろ必要性を示すものである。

これらのプロトコルが使用される通信の種類についての最大の脅威は、ネットワーク上の盗聴{snooping} である。 例えば、この種の通信には、その使用が購入者に制限されるデータベースへのオンラインアクセスを含むかもしれない。 ベーシック認証では、盗聴者はユーザのパスワードを取得できる。 これは、盗聴者にデータベース中のあらゆるものへのアクセスを許す事になるだけでなく、更に悪い事に、そのユーザが同じパスワードで保護するその他のあらゆるものへのアクセスを許す事になるだろう。

一方、ダイジェスト認証では、盗聴者は試問{question} 中の通信へのアクセスを得るが、それはユーザのパスワードではない。 盗聴者によって取得された情報は繰り返し攻撃を許す事になるだろうが、それは同じドキュメントへのリクエストのみか、あるいはそれでさえサーバの nonce の選択によっては制限されているかもしれない。

4.5 繰り返し攻撃

ダイジェスト認証に対する繰り返し攻撃は、盗聴者が繰り返し攻撃で得られる唯一の文書は既に見ているので、単純な GET リクエストには通常無意味だろう。 何故なら、リクエストされた文書の URI はクライアントのリクエストにて要約され、サーバは単にその文書を運ぶだけだからである。 一方、基本認証の下では、一旦盗聴者がユーザのパスワードを得た場合、そのパスワードによって保護される任意の文書が盗聴者に開かれている事になる。

従って、複数の意味で、繰り返し攻撃に対する保護が必要である。 良いダイジェスト認証の実装は様々な方法でこれを行う事が可能である。 "nonce" 値を生成するサーバは実装依存であるが、それがクライアントの IP や、time-stamp、リソースの ETag、また (上記にて推奨されるような) 秘密のサーバキーのダイジェストを含む場合、繰り返し攻撃は単純とはならなくなる。 攻撃者は、そのリクエストが誤った IP アドレスから来ているとサーバに確信させ、更にそのサーバに、サーバがその文書を転送していると信じているアドレスとは異なる IP アドレスにその文書を転送させなければならない。 攻撃は time-stamp が期限切れになるまでの期間しか、成功しないだろう。 nonce 中のクライアント IP や time-stamp のダイジェストは、通信間の状態を維持しないような実装も許す。

繰り返し攻撃の可能性を許容できないようなアプリケーションの場合、サーバは、二次使用は受け入れられないであろう一回きりの nonce 値を使用する事ができる。 これは、nonce の time-stamp (故にそれから作られたダイジェスト) が期限切れになるまで、サーバにどの nonce 値が使用されているかを記憶させるというオーバーヘッドを要求するが、これは繰り返し攻撃に対する防御として有効である。

実装は、POST と PUT のリクエストの繰り返し攻撃の可能性に特別の注意を払わなければならない。 もしサーバが、一回きりの、あるいは他の使用制限がある nonce を必要としなかったり、qop=auth-int の完全なる保護の使用を要求しなければ、攻撃者は、偽造のフォームデータや他のメッセージボディを持つ成功したリクエストからの妥当な証明書を繰り返す事ができた。 完全なる保護の使用でさえ、ヘッダフィールド中の殆どの外部データは保護されない。 適切な nonce の生成やチェックによって、以前に使用された妥当な証明書の繰り返し攻撃からいくらか保護できるようになるが、section 4.8 も参照。

4.6 複数の認証スキームによって生み出される弱点

HTTP/1.1 サーバは、401 (Authenticate) レスポンスへ複数の challenge を返す事ができ、各 challenge は異なる auth-scheme を使用する事ができる。 ユーザエージェントは、自身が理解する最も強い auth-scheme を使用を選択し、またその challenge に基づいたユーザからの証明書を要求しなければならない

多くのブラウザは基本認証のみを理解するだろうという事、そしてそれが提示される最初の auth-scheme である事が要求されるという事に注意せよ。 サーバは最小に受理可能な場合、基本認証のみを含むべきである。

サーバが WWW-Authenticate ヘッダを使用して認証スキームの選択を提示する時、使用される認証の強さはその認証スキームの中で最も弱いものと同じ程度良いもののみである。 複数の認証スキームを利用する特別な攻撃シナリオの議論に関しては、以下の section 4.8 を参照。

4.7 オンライン辞書攻撃

攻撃者は盗聴ができる場合、一般的な単語のリストに対して任意の盗聴したnonce/レスポンス対を試す事ができる。 そのようなリストは、可能なパスワードの総数より遥かに小さい。 リスト上の各パスワードについてのレスポンスを計算するコストは、各 challenge 毎に払われる。

サーバは、ユーザが辞書にあるようなパスワードを選択できないようにする事でこの攻撃を軽減する事ができる。

4.8 中継者{Man in the Middle}

基本認証もダイジェスト認証も共に、例えば、悪意を持つ、あるいはセキュリティ的に妥協されたプロクシからの "中継者{man in the middle}" (MITM) 攻撃に脆弱である。 明らかに、これは盗聴による問題の全てを表すものだろう。 しかし、これは攻撃者に更なる機会をも表すものとなる。

中継者攻撃では、クライアントがユーザの証明書 (例えば、パスワード) を露にするような認証スキームを使用する事を望むように、選択可能なセットにより弱いものを加えるような事が可能になるであろう。 このため、クライアントは、常に提示された選択セットから自身が理解する最も強い認証スキームを使用すべきである。

より高度な MITM 攻撃では、全ての選択セットを削除し、それを基本認証のみを要求するような challenge に置き換え、その上でオリジンサーバが要求する最も強いスキームを使用して認証を行うために基本認証からの平文の証明書を使うであろう。 そのような MITM 攻撃を組み込んだ特に狡猾な手法には、だまされやすいユーザに対して "無料の" プロクシキャッシングサービスを提供するものがあるだろう。

ユーザエージェントは、証明書が要求されている時にどんな認証スキームが使用されているかの視覚的な表示をしたり、サーバによって要求された今までで最も強い認証スキームを記憶するような手段を考慮し、より弱いものを使用する前に警告メッセージを発するべきである。 また、ユーザエージェントが、一般に、あるいは特定のサイトからはダイジェスト認証を要求するように設定される事は良い考えだろう。

あるいは、悪意のあるプロクシは、クライアントが望んだものではなく攻撃者が望むものでリクエストを作る事でクライアントになりすますかもしれない。 もちろん、これは基本認証に対して比較可能な攻撃よりも遥かに困難なものである。

4.9 選択平文攻撃{Chosen plaintext attacks}

ダイジェスト認証では、MITM あるいは悪意のあるサーバは、クライアントがそのレスポンスを計算するために使用するであろう nonce を任意に決定する事ができる。 これは "選択平文" 攻撃と呼ばれる。 nonce を選択する能力は暗号解析をはるかに容易にする事が知られている [8]

しかし、選択平文を使用して、ダイジェスト認証で使用される MD5 一方向関数を解析する方法は、現在知られていない。

この攻撃への対策は、クライアントがオプショナルな "cnonce" 指示子の使用を要求するように設定する事である; これによって、クライアントは攻撃者によって選択されない方法でハッシュへその入力を変更できる。

4.10 Precomputed dictionary attacks

ダイジェスト認証では、攻撃者は選択平文攻撃を実行する事ができれば、攻撃者が選択した nonce に多くの一般的な単語についてのレスポンスを前もって計算し、(レスポンス、パスワード) 対の辞書を格納する事ができる。 そのような前計算はしばしば多くのマシン上で平行して行われるであろう。 そして、その challenge に対応するレスポンスを取得するための選択平文攻撃に使用し、その辞書中からパスワードを探し出すだろう。 たとえ殆どのパスワードが辞書に無いとしても、いくらかはあるかもしれない。 攻撃者はその challenge を盗み取るのだから、そのリスト上の各パスワードのレスポンスを計算するコストは多くのパスワードを見つける事によって釣り合う分けである。 1 億のパスワード/レスポンス対を備えた辞書は、ディスク記憶装置を約 3.2 ギガバイトとるであろう。

この攻撃への対策は、クライアントがオプショナルな "cnonce" 指示子の使用を要求するように設定する事である。

4.11 Batch brute force attacks

ダイジェスト認証では、MITM は選択平文攻撃を実行する事ができれば、同じnonce の多くのユーザからレスポンスを集める事ができる。 そして、そのパスワード空間を通じての単一のパスワードにおける nonce/レスポンス対の一つを生成するようなパスワード空間の任意の部分集合中のパスワードを全て知る事ができる。 それはまた、集められた nonce/レスポンス対の数と等しい因子によって最初のパスワードを見つけるための時間を短縮する。 このパスワード空間の探索はしばしば多くのマシン上で平行して行われ、また一機のマシンでさえ、パスワード空間の大きな部分集合を非常に速く探索する事ができる -- 6 文字以下のパスワードが数時間で全て探索されたというレポートも存在する。

この攻撃への対策は、クライアントがオプショナルな "cnonce" 指示子の使用を要求するように設定する事である。

4.12 偽造サーバによるなりすまし

基本認証は偽造サーバによるなりすましに脆弱である。 もし、ユーザを実際には悪意のあるサーバと接続しているのに、ユーザが知るパスワードによって保護されている情報を含むホストと接続していると信じさせる事ができれば、悪意のあるサーバはパスワードを要求し、後の使用のためにそれを保存しておき、エラーを装う事ができる。 この種の攻撃はダイジェスト認証ではより困難となる -- しかし、クライアントは "中継者" 攻撃に対抗するために、ダイジェスト認証が使用され、そしてそれは恐らく上記にて記述される技術のいくつかの使用を要求するという事を知らなければならない。 更に、ユーザにこの攻撃を発見させるために、各スキームの意味を解釈する中で適切な誘導を使用した認証メカニズムの視覚的表示を行う事ができる。

4.13 パスワードを保存する事

ダイジェスト認証では、認証するエージェント (通常はサーバ) が、与えられた realm に関連付けられた "パスワードファイル" 中にユーザの名前やパスワードに基づいたくつかのデータを保存する必要がある。 通常、これはユーザ名と H(A1) から成る一対となり、ここで H(A1) は上に記述されるようにユーザ名、realm、パスワードのダイジェストされた値である。

このセキュリティ的意味は、もしこのパスワードファイルが危険に晒された場合、攻撃者はこの realm を使用してサーバ上の文書への即時アクセスが可能という事である。 標準の UNIX のパスワードファイルとは違い、この情報はこのファイルに関連づけられたサーバ領域の文書にアクセスするために解読する必要はない。 一方で、ユーザのパスワードを得るためには、解読や総当たり攻撃{brute force attack} が必要であろう。 それは、realmがパスワードファイルに保存されるダイジェストされたデータの一部であるからである。 それは、あるダイジェスト認証パスワードファイルが危険に晒された場合、同じユーザ名やパスワードを持つその他のものを危険に晒す事はない (しかし総当たり攻撃には晒される) という事を意味する。

ここに、二つの重要なセキュリティ的結論がある。 初めに、そのパスワードファイルは、自身の領域中の文書にアクセスする事が有効に行われるので、まるでそれが暗号化されていないパスワードを含んでいるかのように、保護されなければならない。

次なる結論は、realm 文字列は任意の単一ユーザが使用するような全ての領域中でユニークであるべきであるという事である。 特に、realm 文字列は、認証を行うホストの名前を含むべきである。 サーバを認証するためのクライアントの無力さ{inability} はダイジェスト認証の弱さとなる。

4.14 要約

現代の暗号の標準により、ダイジェスト認証は弱いものである。 しかし、大きな目的の範囲において、基本認証の置換たる価値はある。 これは、基本認証の弱点を、全てではないが、いくらか改善するものである。 その強さは、実装に依存して変化するであろう。 特に、(サーバ実装に依存する) nonce の構造は、繰り返し攻撃を仕掛ける容易さに影響するかもしれない。 例えば、ある範囲のサーバオプションは、実装の中には繰り返し攻撃の可能性を取り除くための一度きりの nonce やダイジェストのサーバでのオーバーヘッドの受け入れに好意的であるだろうから、適切である。 他のものは、単一の IPアドレスや単一の ETag、あるいは時間的に制限された、上記にて推奨されたような nonce に満足するかもしれない。

最終的に、あらゆるこの文書に従う実装は相対的に暗号の標準より弱くなるであろうが、あらゆるこの文書に従う実装は基本認証よりはるかに優れたものになるであろう。

5 サンプルコード

以下のコードは、H(A1), H(A2), request-digest, response-digest の各値の計算、そしてsection 3.5 の例にて使用される値を計算するテストプログラムを提供する。 これは、RFC 1321 からの MD5 実装を使用する。

    File "digcalc.h":

 #define HASHLEN 16
 typedef char HASH[HASHLEN];
 #define HASHHEXLEN 32
 typedef char HASHHEX[HASHHEXLEN+1];
 #define IN
 #define OUT

 /* calculate H(A1) as per HTTP Digest spec */
 void DigestCalcHA1(
     IN char * pszAlg,
     IN char * pszUserName,
     IN char * pszRealm,
     IN char * pszPassword,
     IN char * pszNonce,
     IN char * pszCNonce,
     OUT HASHHEX SessionKey
     );

 /* calculate request-digest/response-digest as per HTTP Digest spec */
 void DigestCalcResponse(
     IN HASHHEX HA1,           /* H(A1) */
     IN char * pszNonce,       /* nonce from server */
     IN char * pszNonceCount,  /* 8 hex digits */
     IN char * pszCNonce,      /* client nonce */
     IN char * pszQop,         /* qop-value: "", "auth", "auth-int" */
     IN char * pszMethod,      /* method from the request */
     IN char * pszDigestUri,   /* requested URL */
     IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" */
     OUT HASHHEX Response      /* request-digest or response-digest */
     );

 File "digcalc.c":

 #include <global.h>
 #include <md5.h>
 #include <string.h>
 #include "digcalc.h"

 void CvtHex(
     IN HASH Bin,
     OUT HASHHEX Hex
     )
 {
     unsigned short i;
     unsigned char j;

     for (i = 0; i < HASHLEN; i++) {
         j = (Bin[i] >> 4) & 0xf;
         if (j <= 9)
             Hex[i*2] = (j + '0');
          else
             Hex[i*2] = (j + 'a' - 10);
         j = Bin[i] & 0xf;
         if (j <= 9)
             Hex[i*2+1] = (j + '0');
          else
             Hex[i*2+1] = (j + 'a' - 10);
     };
     Hex[HASHHEXLEN] = '\0';
 };

 /* calculate H(A1) as per spec */
 void DigestCalcHA1(
     IN char * pszAlg,
     IN char * pszUserName,
     IN char * pszRealm,
     IN char * pszPassword,
     IN char * pszNonce,
     IN char * pszCNonce,
     OUT HASHHEX SessionKey
     )
 {
       MD5_CTX Md5Ctx;
       HASH HA1;

       MD5Init(&Md5Ctx);
       MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
       MD5Update(&Md5Ctx, ":", 1);
       MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
       MD5Update(&Md5Ctx, ":", 1);
       MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
       MD5Final(HA1, &Md5Ctx);
       if (stricmp(pszAlg, "md5-sess") == 0) {
             MD5Init(&Md5Ctx);
             MD5Update(&Md5Ctx, HA1, HASHLEN);
             MD5Update(&Md5Ctx, ":", 1);
             MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
             MD5Update(&Md5Ctx, ":", 1);
             MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
             MD5Final(HA1, &Md5Ctx);
       };
       CvtHex(HA1, SessionKey);
 };

 /* calculate request-digest/response-digest as per HTTP Digest spec */
 void DigestCalcResponse(
     IN HASHHEX HA1,           /* H(A1) */
     IN char * pszNonce,       /* nonce from server */
     IN char * pszNonceCount,  /* 8 hex digits */
     IN char * pszCNonce,      /* client nonce */
     IN char * pszQop,         /* qop-value: "", "auth", "auth-int" */
     IN char * pszMethod,      /* method from the request */
     IN char * pszDigestUri,   /* requested URL */
     IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" */
     OUT HASHHEX Response      /* request-digest or response-digest */
     )
 {
       MD5_CTX Md5Ctx;
       HASH HA2;
       HASH RespHash;
        HASHHEX HA2Hex;

       // calculate H(A2)
       MD5Init(&Md5Ctx);
       MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
       MD5Update(&Md5Ctx, ":", 1);
       MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
       if (stricmp(pszQop, "auth-int") == 0) {
             MD5Update(&Md5Ctx, ":", 1);
             MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
       };
       MD5Final(HA2, &Md5Ctx);
        CvtHex(HA2, HA2Hex);

       // calculate response
       MD5Init(&Md5Ctx);
       MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
       MD5Update(&Md5Ctx, ":", 1);
       MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
       MD5Update(&Md5Ctx, ":", 1);
       if (*pszQop) {
           MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
           MD5Update(&Md5Ctx, ":", 1);
           MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
           MD5Update(&Md5Ctx, ":", 1);
           MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
           MD5Update(&Md5Ctx, ":", 1);
       };
       MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
       MD5Final(RespHash, &Md5Ctx);
       CvtHex(RespHash, Response);
 };

 File "digtest.c":


 #include <stdio.h>
 #include "digcalc.h"

 void main(int argc, char ** argv) {

       char * pszNonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093";
       char * pszCNonce = "0a4f113b";
       char * pszUser = "Mufasa";
       char * pszRealm = "testrealm@host.com";
       char * pszPass = "Circle Of Life";
       char * pszAlg = "md5";
       char szNonceCount[9] = "00000001";
       char * pszMethod = "GET";
       char * pszQop = "auth";
       char * pszURI = "/dir/index.html";
       HASHHEX HA1;
       HASHHEX HA2 = "";
       HASHHEX Response;

       DigestCalcHA1(pszAlg, pszUser, pszRealm, pszPass, pszNonce,
 pszCNonce, HA1);
       DigestCalcResponse(HA1, pszNonce, szNonceCount, pszCNonce, pszQop,
        pszMethod, pszURI, HA2, Response);
       printf("Response = %s\n", Response);
 };

6 謝辞

この仕様書が実質的な改訂を経る以前、AbiSource 社の Eric W. Sink はオリジナルの著者のうちの一人であった。

著者に加えて、この文書の作成に有益な議論が、Peter J. Churchyard, Ned Freed, David M. Kristol 各氏からなされた。

Jim Gettys, Larry Masinter 各氏は、最新版のためにこの文書を編集した。

7 参照文献

1
Berners-Lee, T., Fielding, R. and H. Frystyk, Hypertext Transfer Protocol -- HTTP/1.0, RFC 1945, May 1996.
2
Fielding, R., Gettys, J., Mogul, J., Frysyk, H., Masinter, L., Leach, P. and T. Berners-Lee, Hypertext Transfer Protocol --HTTP/1.1, RFC 2616, June 1999.
3
Rivest, R., The MD5 Message-Digest Algorithm, RFC 1321, April 1992.
4
Freed, N. and N. Borenstein, Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies, RFC 2045, November 1996.
5
Dierks, T. and C. Allen The TLS Protocol, Version 1.0, RFC 2246, January 1999.
6
Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P., Luotonen, A., Sink, E. and L. Stewart, An Extension to HTTP: Digest Access Authentication, RFC 2069, January 1997.
7
Berners-Lee, T., Fielding, R. and L. Masinter, Uniform Resource Identifiers (URI): Generic Syntax and Semantics, RFC 2396, August 1998.
8
Kaliski, B.,Robshaw, M., Message Authentication with MD5, CryptoBytes, Sping 1995, RSA Inc, (http://www.rsa.com/rsalabs/pubs/cryptobytes/spring95/md5.htm)
9
Klensin, J., Catoe, R. and P. Krumviede, IMAP/POP AUTHorizeExtension for Simple Challenge/Response, RFC 2195, September 1997.
10
Morgan, B., Alvestrand, H., Hodges, J., Wahl, M., Authentication Methods for LDAP, Work in Progress.

8 筆者のアドレス

 John Franks
 Professor of Mathematics
 Department of Mathematics
 Northwestern University
 Evanston, IL 60208-2730, USA

 EMail: john@math.nwu.edu


 Phillip M. Hallam-Baker
 Principal Consultant
 Verisign Inc.
 301 Edgewater Place
 Suite 210
 Wakefield MA 01880, USA

 EMail: pbaker@verisign.com


 Jeffery L. Hostetler
 Software Craftsman
 AbiSource, Inc.
 6 Dunlap Court
 Savoy, IL 61874

 EMail: jeff@AbiSource.com


 Scott D. Lawrence
 Agranat Systems, Inc.
 5 Clocktower Place, Suite 400
 Maynard, MA 01754, USA

 EMail: lawrence@agranat.com


 Paul J. Leach
 Microsoft Corporation
 1 Microsoft Way
 Redmond, WA 98052, USA

 EMail: paulle@microsoft.com


 Ari Luotonen
 Member of Technical Staff
 Netscape Communications Corporation
 501 East Middlefield Road
 Mountain View, CA 94043, USA


 Lawrence C. Stewart
 Open Market, Inc.
 215 First Street
 Cambridge, MA  02142, USA

 EMail: stewart@OpenMarket.com

9 著作権表示全文

Copyright © The Internet Society (1999). All Rights Reserved.

この文章とその翻訳は、複製し他人に配布する事ができ、またその実装についてのコメント、その他の方法を用いた説明、その補助となるような派生的作業はそれらの中に上の著作権表示とこの段落を含む事によって、その全て又は一部を、いかなる制約も受けずに、作成、複製、発表、及び配布する事ができる。 しかしながら、インターネット標準化プロセスにて定義されている著作権のための手続きに従わなければならないような場合の中でインターネット標準を開発するという目的に必要である、あるいは英語以外の言語に翻訳する必要があるという場合を除いて、この文章自体を、その著作権表示や、インターネット学会あるいは他のインターネット団体への参照を削除するような、いかなる変更もできない。

上で認めた制限された許諾は永続的なものであり、インターネット学会及びその継承者や譲渡者によって取り消される事は無い。

この文書とここに含まれた情報は、"そのまま {AS IS}" である事を基に提供され、インターネット学会、及び IETF は、この中の情報の使用が、商用利用及び特定用途においていかなる権利もいかなる暗黙的保障も侵害していないという保障への制限を含め、明示的に又は暗黙的に、全ての保障を放棄する

謝辞

RFC Editer 機構の資金は、現在インターネット学会から提供されている。


#TOP
Copyright © 1999-2008 橋本英彦 (H-Hash), All Rights Reserved.