OAuth 2.0 Security Best Current Practice draft 21-24のdiffを読む

## はじめに

はじめまして。[task4233](https://task4233.dev)です。

本記事は、[Digital Identity技術勉強会 #iddance Advent Calendar 2023](https://qiita.com/advent-calendar/2023/iddance)の14日目の記事です。昨日の記事は、cm-ayfさんによる[エンタープライズサインインとソーシャルサインインの兼ね合いについて](https://qiita.com/cm-ayf/items/19b5c50c8a329e563cb0)でした。

一方の僕は何を書こうかなと思っていたのですが、今年のアドカレではセキュリティ系の話題が無さそうなので、2016年の11月から議論されている[OAuth 2.0 Security Best Current Practice](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics)の昨年からの差分について、まとめることにしました。

差分は[こちらのiddiff](https://author-tools.ietf.org/diff?url_1=https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-21.txt&url_2=https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.txt)から確認できます。大きく変更があったのは以下の2点です。

- Misuse of Stolen Access Tokens
- Attacks on In-Browser Communication Flows

本記事では上記2点について、それぞれ私の解釈に基づく要約を書きます。誤りがあればご指摘のコメントをお待ちしています 🙇

## 本記事の想定読者

本記事は、OAuth 2.0自体は理解しているが最新のdraftには目を通していない人を対象としています。そのため、OAuth 2.0自体の基本概念や各攻撃手法に関する詳しい説明は割愛します。参考文献やリンクは載せておくので、不明点があれば適宜参照してください。

## Misuse of Stolen Access Tokens([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-misuse-of-stolen-access-tok))

前提として、Access Tokenは以下の攻撃手法などで攻撃者に窃取される可能性があります。

- redirect_uriのバリデーション不備([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#insufficient_uri_validation))
- Refererヘッダを介したクレデンシャル漏洩([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#credential_leakage_referrer))
- ブラウザ履歴を介したクレデンシャル漏洩([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#browser_history))
- ミックスアップ攻撃([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#mix_up))
- リソースサーバにおけるアクセストークン漏洩([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#access_token_leakage))

それぞれの攻撃は事前予防策が提案されているものの、策が不十分であったり実装に不備があったりする場合があります。そこで、このような**事前予防策のみならずAccess Tokenが漏洩しても問題がないような事後対策をするべき(SHOULD)である**、というのが本章の趣旨です。

対策としては、Sender-Constrained Access Tokenの利用**と**Audience Restricted Access Tokenの利用が紹介されています。端的に述べると、**前者はAccess Tokenを利用するクライアント(Sender)を制限する手法**、**後者はAccess Tokenが利用されるリソースサーバ(Audience)を制限する手法**です。

それぞれ順番に見ていきます。

### Sender-Constrained Access Token([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-sender-constrained-access-t))

**特定のクライアントのみに利用できるよう制限されたAccess Tokenのこと**です。これにより、Access Tokenが漏洩しても、同時にクライアントのクレデンシャルが漏洩しない限りは、正規のクライアント以外からは利用できなくなります[^1]。

この技術の肝は、Access Tokenリクエスト時およびAccess Token利用時に、認可サーバおよびリソースサーバで、特定のクライアントに紐づくデータを検証することにあります。2023年現在では以下の2つの手法がOAuthワーキンググループによって定義され、実際に利用されています。

| RFC | 利用されるクレデンシャル | Access Tokenに紐づけられる情報 |
| --- | --- | --- |
| [RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound](https://www.rfc-editor.org/rfc/rfc8705.html) | クライアントと認可サーバ間のMutual TLSで用いられるクライアント証明書 | クライアント証明書のハッシュ値 |
| [RFC 9449: OAuth 2.0 Demonstrating Proof of Possession(DPoP)](https://www.rfc-editor.org/rfc/rfc9449.html) | クライアント側の秘密鍵 | クライアント側の秘密鍵に対応する公開鍵のハッシュ値(JWK SHA256 Thumbprint) |

上記の2つの手法では、公開鍵暗号を用いたデジタル署名とその検証がベースにあります。これにより、Access Tokenリクエスト時に用いられたクライアント(が持つクレデンシャル)とAccess Token利用時に用いられたクライアント(が持つクレデンシャル)が同一であることが保証できます。ただし、このSender-Constrained Access Tokenは、クライアントが持つクレデンシャルが漏洩すると意味を為さなくなることに注意してください。


![Sender-Constrainted Access Token Range](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238364/445b4c0c-2a94-2296-a438-d79053962b52.png)

他の手法として、[OAuth Token Binding](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-token-binding-08)や[Signed HTTP Requests](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03)、[JWT Pop Tokens](https://datatracker.ietf.org/doc/html/draft-sakimura-oauth-jpop-05)などのドラフトが過去に議論されましたが、いずれのドラフトもexpiredとなりました。

### Audience Restricted Access Token([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-audience-restricted-access-))

**特定のリソースサーバ(Audience)のみ利用できるように制限されたAccess Tokenのこと**です。これにより、Access Tokenが漏洩したりリソースサーバが偽造されたりしても、正規のリソースサーバ以外でのAccess Token利用ができなくなります[^1]。

このAudienceは、論理的な名前やURLのような物理的なアドレス、リソースサーバのX.509証明書のフィンガープリント等で表現できます。

これを実現するための手法として、[RFC 8707: Resource Indicators for OAuth 2.0](https://www.rfc-editor.org/rfc/rfc8707.html)が紹介されています。この手法は、AuthorizationリクエストおよびAccess Tokenリクエストにおいて `resource` というパラメータで、利用可能なリソースサーバを設定します。これにより、リソースサーバへのアクセス時に、設定されたリソースサーバのみに利用を制限できます。

この説明に関しては、[こちらのまとめ記事](https://qiita.com/TakahikoKawasaki/items/57d6242ff8b20dbff08f)が分かりやすかったです。

![Audience Restricted Access Token Range](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238364/60cb36a9-1c6e-52e7-3c10-40ba7826ac98.png)


### Discussion: Preventing Leakage via Metadata([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-discussion-preventing-leaka))

認可サーバは、安全にAccess Tokenを利用できる場所に関する追加情報を提供することもできます。例えば、Access Tokenレスポンスの結果にAccess Tokenのリソースサーバの例を含めるようにした例は以下の通りです。

```
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "access_token_resource_server":
    "https://hostedresource.somesite.example/path1",
...
}
```

ただし、この手法はクライアントがセキュリティポリシを実施して、正当な宛先にのみアクセストークンを送信することに依存します。現実世界では、クライアント側のセキュリティコントロールが適切に為されていないことが多々あるため、この手法に関するAccess Tokenフィッシングの防止は失敗する可能性が高いと述べられています。更に、セキュリティ関連のロジックを上記のエンティティに移すことが、実行可能なアプローチであるとも述べられています。

## Attacks on In-Browser Communication Flows([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-attacks-on-in-browser-commu))

前提として、Authorizationレスポンスのために、HTTPリダイレクトではなく[postMessage API](https://html.spec.whatwg.org/multipage/web-messaging.html#web-messaging)のようなIn-Browser Communicationを用いることが可能です。この**In-Browser Communicationのメリットは、Access Tokenの漏洩リスクを下げられること**です([ref](https://security.stackexchange.com/questions/144591/what-is-the-security-benefit-of-using-postmessage-instead-of-a-callback-url-in-o))。HTTPリダイレクトを利用すると、前述の通り、ブラウザ履歴を介した漏洩やRefererヘッダを介した漏洩のリスクがあります。しかし、In-Browser Communicationとすることで、そもそもAccess TokenがURLの一部として含まれることがないため、ブラウザ履歴を介した漏洩が起きなくなります。

一方で、このAPIを利用することで、**メッセージが悪意のあるOriginに送信されたり、悪意のあるOriginからインジェクトされたりする可能性がある**という趣旨の章です。[MDNのドキュメント](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)によると、postMessage APIは `postMessage(message, targetOrigin)` というinterfaceを持ちます。 `message` は他のウィンドウへ送られるデータ、 `targetOrigin` はイベントを配信する先のOriginを意味します。そのため、 `targetOrigin` のインジェクトに対するリスクを考慮する必要がある訳です。

ここからは、その例について見ていきましょう。

### 不十分なReceiver Originの制限([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-insufficient-limitation-of-))

`postMessage(message, targetOrigin)` の `targetOrigin` に、ワイルドカード `*` が設定されている場合、認可サーバはクライアントのOriginではなく、任意のOriginにレスポンスを送信します。レスポンスの送信先windowが攻撃者によって制御される場合、攻撃者はレスポンスを読むことができ、Authorization Codeが漏洩します。

```jsx
window.opener.postMessage(
  {
    code: "ABC",
    state: "123"
  },
  "*" // window.opener内の任意のWebサイトがmessage内容を受け取ることができる
)
```

MDNのドキュメントでも、このワイルドカードの利用に関して以下の通り注意喚起されています。

> **Always provide a
specific `targetOrigin`, not `*`, if you know where the other
window's document should be located. Failing to provide a specific target discloses
the data you send to any interested malicious site.
ref:** [https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#targetorigin](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#targetorigin)
> 

### 不十分なURIバリデーション([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-insufficient-uri-validation))

不十分なReceiver Originの制限と同様に、 `targetOrigin` のバリデーションが不十分だと攻撃者の提供したOriginへリダイレクトされる可能性があります。例えば、`https:\/\/.*\.?mydomain.com\/.*` という正規表現を利用する場合、 `https://evil.com/mydomain.com/` へのリダイレクトを許してしまいます([ref](https://regex101.com/r/TGczq9/1))。

そのため、以下のようなリクエストが通る可能性があります。

```jsx
window.opener.postMessage(
  {
    code: "ABC",
    state: "123"
  },
  "https://evil.com/mydomain.com/" // 文字列完全一致の比較アルゴリズムを利用すべき
)
```

詳しくは、HTTPリダイレクトに関する章で説明されているので、良ければ参照してください([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-insufficient-redirect-uri-v))。

### Sender Originの不十分なバリデーション後のインジェクション([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-injection-after-insufficien))

`postMessage` によるAuthorizationレスポンスやAccess Tokenレスポンスを期待するクライアントは、messageのSender Originを検証しない可能性があります。そのため、攻撃者はこれらのレスポンスをクライアントにインジェクトしてCSRF攻撃に繋げられる可能性があります。この辺りの記述はCSRFに関する章で説明されているので、良ければ参照してください([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-cross-site-request-forgery))。

### 推奨設定([ref](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html#name-recommendations-2))

基本的にReceiver Originを事前登録して、API呼び出し前に文字列の完全一致でバリデーションしなければならない(MUST)と書かれています。そのため、当然ワイルドカードの利用もしてはならない(MUST NOT)と書かれています。

また、Sender Originに関しては、以下の通りクライアントが `postMessage` APIを呼び出す前にヴバリデーションを実施する必要があると書かれています。

```jsx
window.addEventListener("message", (e) => {
  // 正確な認可サーバのOriginのバリデーションをする
  if (e.origin === "https://honest.as.example") {
    // e.data.codeおよびe.data.stateの処理を行う
  }
})
```

## おわりに

今回詳しく紹介しなかったものも含め、OAuth 2.0における脅威を新たに知る機会となりました。特に、ブラウザ履歴からAuthorization Codeを取得するという手法は面白い手法だなと思いました。元々、RFC 6749 The OAuth 2.0 Authentication Frameworkや、RFC 6819 OAuth 2.0 Threat Model and Security Considerationsしか存在を知らなかったので、新しい情報に触れる機会になったので良かったと思います。

対策に関しては、クライアント側で行われるのではなく、今回紹介したようなSender-Constrained Access Tokenのようにサーバ側できちんと検証されるような仕組みを導入していくことが、全体のセキュリティレベル向上に寄与するのかなと思いました。

## 参考文献
- [OAuth 2.0 Security Best Current Practice](https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-24.html)
- [RFC 9449 OAuth 2.0 Demonstrating Proof of Possession (DPoP)](https://www.rfc-editor.org/rfc/rfc9449.html)
- [図解 DPoP (OAuth アクセストークンのセキュリティ向上策の一つ)](https://qiita.com/TakahikoKawasaki/items/34c82fb5c0595b6fc289)
- [RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens](https://www.rfc-editor.org/rfc/rfc8705.html)
- [RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens メモ](https://qiita.com/KWS_0901/items/bfcd2645a28c21fc8108)
- [Resource Indicators for OAuth 2.0](https://qiita.com/TakahikoKawasaki/items/57d6242ff8b20dbff08f)
- [Window: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
- [RFC 6749 The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html)
- [RFC 6819 OAuth 2.0 Threat Model and Security Considerations](https://www.rfc-editor.org/rfc/rfc6819.html)


[^1]: 正しく実装・運用されている場合。