作って学ぶリバースプロキシ⑦RFCベースでDigest認証とBasic認証を比較してみる

## これは何?

前回

https://qiita.com/sigma_devsecops/items/058bf554af0cb25fb9e4

前々回

https://qiita.com/sigma_devsecops/items/7726c643b5c65f654d9a

前回Basic認証を作ってみて，どんなヘッダが流れているか等を理解することができました。
今回は，Basic認証の脆弱な部分をカバーしたDigest認証というのがあるらしいので，Digest認証について調べてみた。

---

## Digest認証とは

[RFC7616](https://datatracker.ietf.org/doc/html/rfc7616#section-3.3)ベースで記載します。
サーバ→クライアントの通信とクライアント→サーバの通信に分けて記載します。

### サーバ→クライアントの通信(チャレンジリクエスト)

#### Basic認証と同じ部分

> The details of the challenge-response authentication mechanism are specified in the "Hypertext Transfer Protocol (HTTP/1.1): Authentication" [RFC7235].

- チャレンジレスポンス形式には，[RFC7235](https://datatracker.ietf.org/doc/html/rfc7235)で定義されているHypertext Transfer Protocol (HTTP/1.1)を使用している。

> If a server receives a request for an access-protected object, and an acceptable Authorization header field is not sent, the server    responds with a "401 Unauthorized" status code and a WWW-Authenticate header field with Digest scheme as per the framework defined above.

- Digest認証ではBasic認証と同様，保護された領域(realmで定義)に対してリクエストが行われた場合に，Authorizationヘッダがなければ401 UnauthorizedとWWW-Authenticateヘッダフィールドのついたレスポンスを返す。

> realm        
> A string to be displayed to users so they know which username and password to use.  This string should contain at least the name of the host performing the authentication and might additionally indicate the collection of users who might have access. 

- WWW-Authenticateはrealmを通して，ユーザがどのユーザ名とパスワードを使うべきかを通知する。realmには，認証を実行するホスト名を記載が推奨されるらしい。

---

#### Basic認証との差分

>  The Digest scheme is based on a simple challenge-response paradigm. The Digest scheme challenges using a nonce value and might indicate that username hashing is supported.
> A valid response contains an unkeyed digest of the username, the password, the given nonce value, the HTTP method, and the requested URI.
> In this way, the password is never sent in the clear, and the username can be hashed, depending on the indication received from the server.  The username and password must be prearranged in some fashion not addressed by this document.

- nonce値を使ってチャレンジする
- パスワードがハッシュ化されて送信される。

これらを実現可能にするために，Digest認証ではWWW-Authenticateヘッダフィールドに要素が追加されている。

##### nonce

> nonce
 A server-specified string which should be uniquely generated each time a 401 response is made.It is advised that this string be Base64 or hexadecimal data. Specifically, since the string is passed in the header field lines as a quoted string, the double-quote character is not allowed, unless suitably escaped. The contents of the nonce are implementation dependent. The quality of the implementation depends on a good choice.
 
- nonce: 401 response時にサーバ側でランダムに生成される一意の文字列。ユーザがパスワードを送信する際にパスワードとnonceから生成したハッシュを送信することで通信が傍受されてもパスワードが漏洩しないようにする目的で使われる。
    - base64デコードもしくは16進数データ推奨。ダブルクオートはエスケープ使用できない

> A nonce might, for example, be constructed as the Base64 encoding of
> timestamp H(timestamp ":" ETag ":" secret-data)
> where timestamp is a server-generated time, which preferably includes micro- or nanoseconds, or other non-repeating values; ETag is the value of the HTTP ETag header field associated with the requested entity; and secret-data is data known only to the server.
> With a nonce of this form, a server would recalculate the hash portion after receiving the client authentication header field and reject the request if it did not match the nonce from that header field or if the timestamp value is not recent enough. In this way, the server can limit the time of the nonce's validity. The inclusion of the ETag prevents a replay request for an updated version of the resource. 

上記の実装例だとnonceは以下のようにして作製される(あくまで実装例なのでこのように実装しないといけないわけではない)。

1. timestamp，Etag，secret-dataを「:」で結合した文字列を作る: secret-dataはサーバのみが知る値
2. 1で作製した文字列のハッシュを計算する(secret-dataが漏洩しないようにするため)
3. 2で作製した文字列をbase64でエンコードする

timestampを使うことでランダム性を，secret-dataを使うことで他のサーバとかぶらないようにしつつ，ETagでリソースの更新を検知できるため，古いリクエストを再利用するリプレイアタックを防ぐことができる。

::: note info
ETagについて補足
> ETag （またはエンティティタグ）は HTTP のレスポンスヘッダーで、リソースの特定バージョンの識別子です。ウェブサーバーは、コンテンツが変更されていない場合はレスポンス全体を再送する必要がないので、キャッシュがより効率的になり通信帯域を節約することができます。[Mmdn web docs](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/ETag)
:::

##### opaque

> opqque
> A string of data, specified by the server, that SHOULD be returned  by the client unchanged in the Authorization header field of subsequent requests with URIs in the same protection space.
> It is RECOMMENDED that this string be Base64 or hexadecimal data. 

- 同じ認証領域に対しては，WWW-Authentiateによって渡されたopaqueの値を変更せずにAuthorizationヘッダにつけて返されるべきである
- これを使って複数の認証領域をサーバ側で区別できる? TODO: 調べる

##### stale

> stale      
> A case-insensitive flag indicating that the previous request from the client was rejected because the nonce value was stale.  If  stale is true, the client may wish to simply retry the request  with a new encrypted response, without re-prompting the user for a  new username and password.  
> The server SHOULD only set stale to true if it receives a request for which the nonce is invalid.  If  stale is false, or anything other than true, or the stale parameter is not present, the username and/or password are invalid, and new values MUST be obtained. 

- 大文字小文字を区別しないでtrue，falseが格納される。
- true: クライアントから送られてきたnonceのみが古い場合は，再認証を要求せずに認証を通す。
- true以外: 再認証が要求され，再度ID/passwordの入力が必要。

##### algorithm

> algorithm
> A string indicating an algorithm used to produce the digest and an  unkeyed digest.  If this is not present, it is assumed to be "MD5".  If the algorithm is not understood, the challenge SHOULD be ignored (and a different one used, if there is more than one).        
> When used with the Digest mechanism, each one of the algorithms  has two variants: Session variant and non-Session variant.
> The  non-Session variant is denoted by "<algorithm>", e.g., "SHA-256", and the Session variant is denoted by "<algorithm>-sess", e.g.,   "SHA-256-sess".

- ダイジェストの生成に使用されるアルゴリズムを示す文字列
    - 鍵ありダイジェスト: ID，パスワード等の鍵を含む
    - 鍵なしダイジェスト: サーバから送られてきた値をそのまま返す
- オプションなしの場合はMD5とみなされる。
- 使用されるアルゴリズムがわからない場合はチャレンジは無視されるべきである。
- 各アルゴリズムには2種類のタイプがある
    - セッションバリアント: nonceやcnonce等を組み合わせた上で認証情報をハッシュ化してダイジェストを作る。e.g. SHA-256-sess 
    - 非セッションバリアント: 認証情報を直接ハッシュ化してつくる。e.g. SHA-256
- 鍵ありダイジェストをKD(seret, data)，鍵なしダイジェストをH(date)

##### qop

> qop
> This parameter MUST be used by all implementations.  It is a quoted string of one or more tokens indicating the "quality of       protection" values supported by the server.
> The value "auth" indicates authentication; the value "auth-int" indicates authentication with integrity protection.
> See the descriptions below for calculating the response parameter value for the application of this choice.  Unrecognized options MUST be ignored. 

- 必須パラメータであり，サーバの保護クオリティを表す，引用符で囲まれた文字列。サーバはサポートするタイプを列挙し，クライアントはどれかを選択する
    - auth: 認証を表す
    - auth-int: 統合保護認証を表す

:::note info
[Digest認証のパラメータを眺めた](https://unarist.hatenablog.com/entry/2015/09/25/013142)を見た感じだと，auth-intはリクエストボディもハッシュ計算に使われるらしい。
:::


### クライアント→サーバの通信(レスポンス)

Authorization Headerを使うのは同じですが，WWW-Authenticateと同様に追加のパラメータがある

#### response

> response
> A string of the hex digits computed as defined below; it proves that the user knows a password.

responseにはクレデンシャルのハッシュである。

#### username

usernameがないとサーバ側はハッシュだけでは，ユーザが誰か判別できない。

#### realm

サーバ→クライアントと同じ内容なので省略

#### uri

> uri
> The Effective Request URI (Section 5.5 of [RFC7230]) of the HTTP request; duplicated here because proxies are allowed to change the request target ("request-target", Section 3.1.1 of [RFC7230]) in transit.

proxyがrequest-targetを上書きするので上書き防止らしい

#### qop

> qop
> Indicates what "quality of protection" the client has applied to the message.  Its value MUST be one of the alternatives the server indicated it supports in the WWW-Authenticate header field. 

サーバ側で対応しているサーバ保護クオリティからどれか一つ選択して使用する。

#### cnonce

> cnonce
> This parameter MUST be used by all implementations. The cnonce value is an opaque quoted ASCII-only string value provided by the client and used by both client and server to avoid chosen plaintext attacks, to provide mutual authentication, and to provide some message integrity protection.  See the descriptions below of the calculation of the rspauth and response values.

- クライアント側で生成するnonce
- 選択平文攻撃に対する耐性が上がる(サーバになりすまして，nonce空にすることはできても，cnonceは制御できないということだと思われる)。

:::note info
選択平文攻撃: 
> 任意の平文に対応する暗号文が得られる条件で、暗号文から平文を求める攻撃である。[Wikipedia暗号解読](https://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E8%A7%A3%E8%AA%AD)
:::

#### nc

> nc
> This parameter MUST be used by all implementations.
> The nc parameter stands for "nonce count". The nc value is the hexadecimal count of the number of requests (including the current request) that the client has sent with the nonce value in this request.
> For example, in the first request sent in response to a given nonce value, the client sends "nc=00000001". The purpose of this parameter is to allow the server to detect request replays by maintaining its own copy of this count

- ncはnonce countの略でサーバに何回認証リクエストを送ったかを16進数で記録する
- 実装必須
- ncを使うことでリプレイ攻撃を検知できる。

---

## 認証情報の検証方法

一旦qop=authの場合について書いておきます。
Basic認証では基本的にpasswordが平文なのでそのまま比較するだけでしたが，Digest認証ではpasswordを以下の手順でハッシュ化した`response`という値を使って検証を行います。

>  response = <"> < KD ( H(A1), unq(nonce)
>                               ":" nc
>                               ":" unq(cnonce)
>                               ":" unq(qop)
>                               ":" H(A2)
>                      ) <">

> 3.4.2.  A1
> If the algorithm parameter's value is "<algorithm>", e.g., "SHA-256", then A1 is:
> A1       = unq(username) ":" unq(realm) ":" passwd

> 3.4.3.  A2
> If the qop parameter's value is "auth" or is unspecified, then A2 is:
> A2       = Method ":" request-uri

※unq()は""を外すという意味です。