こんばんは、ritouです。
今回紹介する仕様は、RFC7662 OAuth Token Introspectionです。
ざっくり言うと
Token Introspectionとかいうと、Token置換攻撃対策としてのClient-AuthZ Server間のAccess Token検証を想像する方もいるかもしれませんが、この仕様はそれとはあまり関係ありません。
この仕様はOAuthのProtected Resourceのための仕様です。
RFC 6749 - The OAuth 2.0 Authorization FrameworkのRoleのところには、resource serverがprotected resourceをホストしていて、 access tokenを用いたリクエストを受ける、とあります。
簡単に言うと、APIサーバのことですね。
AuthZ ServerとResource Serverが分かれることはよくある話です。
小規模なところは、裏で同じDBを見に行くなどしてAccess Tokenの検証をすればよいわけですが、そこそこの規模のサービスだとか、ID統合など諸々の理由でそういうつくりができないこともあるでしょう。
また、最近はなんたらAPI Gatewayみたいなやつが最初にリクエストを受け、Resource Serverに来る前にAccess Tokenの検証ができるみたいな構成も聞きます。
この仕様はそういうのではなく、"Resource Serverが受け取ったAccess Tokenの状態をAuthZ Serverに問い合わせる"ような状況において、Access Tokenの送り方(問い合わせ方)を定義したものです。
ここからは、RFCの構成を意識しながら説明していきます。
課題
Introductionに書いてあります。
- OAuth 2.0の仕様で、Access Tokenは、"The string is usually opaque to the client."となっている
- Protected Resourceの処理には、Tokenが有効かどうかや紐づいたユーザー情報、認可に関する情報などが必要である
- しかし、OAuth2.0ではProtected Resourceがそれらを知る方法が具体的に定義されていないので、JWTとか独自の内部処理とかでの対応が必要となる(ので、例えばこんな提案とかが出回るわけです→OAuth 2.0のAccess TokenへのJSON Web Token(JSON Web Signature)の適用 - r-weblife)
この仕様が定めること
- Protected ResourceがClientから受け取ったTokenのメタデータをAuthZ Serverに要求する方法
- メタデータはTokenの有効性や許可されたアクセス範囲、認可情報などを含む
- Token文字列自体がメタデータを含んでいるかどうかにかかわらず、この方法を利用できる
Protected Resourceが扱うのはAccess Tokenですが、仕様上はAccess TokenだけではなくRefresh Tokenでもいいみたいです。
Introspection Endpoint
AuthZ ServerはProtected Resourceからの要求を受けるエンドポイントを用意する必要があります。
AuthZ ServerがProtected Resourceにこのエンドポイントをどうやって教えるかなどは仕様の対象外です。
まずはリクエストの説明からです。
Introspection Request
Protected Resourceは"application/x-www-form-urlencoded"形式で次のパラメータを持つHTTP POSTなリクエストを送ります。
- token : REQUIRED。access_token、refresh_tokenなどの値を指定します。
- token_type_hint : OPTIONAL。RFC 7009 - OAuth 2.0 Token Revocationで定義されており、"access_token", "refresh_token"の値が入る。
token scanning attackを防ぐため、このエンドポイントにアクセスするためのProtected Resource - AuthZ Server間の認可が必須です。
この認可については仕様対象外なので何でもいいんですが、OAuth 2.0の仕様に準拠させた例が載っています。
説明すると逆に混乱しそうですが、"AuthZ Serverにとって、Protected ResourceはIntrospection endpointにアクセスしてくるClientである"という感じです。
# client authenticationと組み合わせた例 POST /introspect HTTP/1.1 Host: server.example.com Accept: application/json Content-Type: application/x-www-form-urlencoded Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW token=mF_9.B5f-4.1JqM&token_type_hint=access_token # bearer tokenを用いる例 POST /introspect HTTP/1.1 Host: server.example.com Accept: application/json Content-Type: application/x-www-form-urlencoded Authorization: Bearer 23410913-abewfq.123483 token=2YotnFZFEjr1zCsicMWpAA
次はレスポンスの説明です。
Introspection Response
レスポンスはJSON("application/json")形式です。
HTTP/1.1 200 OK Content-Type: application/json { "active": true, "client_id": "l238j323ds-23ij4", "username": "jdoe", "scope": "read write dolphin", "sub": "Z5O3upPC88QrAjx00dis", "aud": "https://protected.example.net/resource", "iss": "https://server.example.com/", "exp": 1419356238, "iat": 1419350238, "extension_field": "twenty-seven" }
必須なのは、activeという値(boolean)で、Tokenが有効かどうかが入ります。
それ以外で気になるのがあったら仕様を読みましょう。
JWTのClaimについてはRFC 7519 - JSON Web Token (JWT)を読むといいでしょう。
Security Considerationsも流し読みしておきます。
Security Considerations
- AuthZ ServerのTokenチェックをしっかりやろう
- 前述したような攻撃を防ぐために、Protected Resourceの認証、Introspection Endpointへアクセスできるかどうかの確認をしっかりやろう
- TLS/SSLのサーバーチェックとかしっかりやろう
- AuthZ Serverのサーバーサイドのログからの漏えいを防ぐため、GETじゃなくPOST
- キャッシュすると無効化の同期が...
- Token文字列が構造化されてたらどうこう...
みたいなことが書いてあります。
実際こういうしくみを作ろうと思うと、負荷のあたりは悩ましげな感じですねー。
Privacy Considerations
レスポンスにセンシティブなプライバシー情報を含む場合があるのでほげほげ。