🕺

OAuthで使われている "サービス間のデータのやりとり"

2021/01/06に公開

ritou です。

異なるサービス間のデータのやりとり

前回の記事にちょっと書きましたが、OAuthにおいてUser-Agent(ブラウザ)経由で異なるサービス間を行ったり来たりする部分があります。

その際に、当然データのやりとりが行われているわけですが、今日はここに注目します。
2つのサービスがある時に、どのようにデータをやりとりするかを整理することで一般的なWebアプリケーション開発にも生かせるかもしれません。

5つの方法

今回は5つの方法を紹介します。

の前に、2種類の通信方法が出てきます。

  • FrontChannel : User-Agentを経由したリダイレクト
  • BackChannel : 2つのサービスのサーバ間でのやりとり

ここから紹介するやり方は、これらを単体で利用、もしくは組み合わせて利用します。

1. FrontChannel GET

いわゆる クエリパラメータのついたリダイレクト です。
RFC 6749 - The OAuth 2.0 Authorization Framework の Authorization Code Flow での認可リクエスト/レスポンスのやりとりで使われています。

特徴としては

  • 実装は一番簡単
  • ブラウザの履歴に残ったり、リロードで何回も同じリクエストが送られるかもしれない
  • ユーザーが意図的もしくは意図せずにクエリパラメータの値を変えてしまうかもしれない

という感じなので、要件によってはこのやり方が適切ではない場合もあるでしょう。

2. FrontChannel POST(HTML Form)

クエリパラメータではなく、HTML Formとかを使ったPOSTリクエストで送ります

Final: OAuth 2.0 Form Post Response Modeという仕様にて、認可レスポンスをこの方法で送っています。

  • HTML Formを出力 + 自動でSubmitみたいな実装が必要
  • ブラウザの履歴にパラメータは残らず、リロードとかで何度も送ろうとすると再度送りますか?みたいなワーニングが出る場合もある
  • ユーザーが意図的もしくは意図せずにデータの値を変えることも可能
  • クロスサイトのPOSTリクエストとなるため、送り先のHTTP Cookieの挙動に気をつける必要がある(SameSite Attributes)

3. FrontChannel GET/POST w/ JWT(署名つき or 暗号化)

1,2で送られるデータをJWTにします

Final: OpenID Connect Core 1.0 incorporating errata set 1request オブジェクトを用いた認証リクエスト、The OAuth 2.0 Authorization Framework: JWT Secured Authorization Request (JAR)の認可リクエスト、Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)の認可レスポンスなどでこのやり方が使われています。

当然、1,2に加えてJWT特有の特徴が追加されます。JWTと言っても署名つきとか暗号化とかがありますが、

  • 署名つき(JWS) : 改ざんを検知可能。ただしユーザーがJWTのPayloadの値を確認できる
  • 暗号化(JWE) : Payloadに含まれる値を隠せる

という感じなので、この辺りは要件と照らし合わせて使い分けがされると良さそうです。

ここまでの1,2,3は BackChannelのリクエストが送られないため、ネットワーク越しの通信に制限があってもブラウザの遷移のみで利用できる という特徴もあります。

4. FrontChannel GET/POST + BackChannel GET

ここからは、BackChannelなやりとりを加えたパターンとなります。
最初にFrontChannelでURLなりパラメータなりを送りつつ、受け取った側がBackChannelなリクエストで中身を取りに行く感じ です。この場合、データを受け取る側からのリクエストも必要です。

Final: OpenID Connect Core 1.0 incorporating errata set 1request_uri パラメータを用いた認証リクエストで使われています。

特徴としては

  • パラメータがURLだったりパラメータの扱いのルールが決まっている場合、ユーザーがパラメータを見てどんなデータがやりとりされるのかを把握することも 不可能ではない場合がある
  • ユーザーが意図的もしくは意図せずにデータの値を変えることも可能
  • データのやりとりをワンタイムにすることも可能
  • BackChannelでの通信に認証機能を持たせることもできる

と言ったあたりがあるでしょう。

ユーザーがURLを置き換える可能性については、URLのルール(完全一致、前方一致)を決めて検証したり、GETでアクセスした結果をJWTにして署名検証することで送る側が用意したものであることを確認する必要があるかもしれません。

5. BackChannel POST + FrontChannel GET/POST

これは 最初にBachChannelのPOSTリクエストでデータを送りつつ、そこで取得した識別子なりをFrontChannelで送る やり方です。データを送る側からのリクエストのみで完結しますね。

draft-ietf-oauth-par-05 - OAuth 2.0 Pushed Authorization Requests(いわゆるPAR)の認可リクエストで使われています。あと OAuth 1.0 の Temporary Credentials(Request Token/Request Token Secret)の取得からのAuthorization URLみたいなところもこれですね。

4と特徴はほぼ同じです。

  • ユーザーがパラメータを見てどんなデータがやりとりされるのかを把握することも 不可能ではないが、後からBackChannelなリクエストが行われる4よりも困難な作りにしやすい
  • ユーザーが意図的もしくは意図せずにデータの値を変えることも可能
  • データのやりとりをワンタイムにすることも可能
  • BackChannelでの通信に認証機能を持たせることもできる

うまく設計して、受け取る側は内部で識別子を検証するようにすれば、やりとりされるデータをユーザーからも隠したり、ユーザーがパラメータを変更したりしても検知して拒否することも可能でしょう。

まとめ

今回は5つのやり方を紹介しましたが、さらに細かく組み合わせることもできます。

例えば4で紹介した Final: OpenID Connect Core 1.0 incorporating errata set 1request_uri パラメータを用いた認証リクエストでは、BackChannelのリクエストに対するレスポンスがJWT形式になっており、そのデータ自体の発行者などを検証可能です。

紹介した仕様は金融関連の処理に対するプロファイルであるFAPIの仕様にも含まれているものがあります。
これは、よりセンシティブなデータをセキュアにやりとりしたい場合のデータの保護に使えるということなので、ID連携以外のユースケースでも生かせそう、もしくは気づかずに使ってたわーということもあるかもしれません。
異なるサービス間でデータのやりとりを考える際には気に留めておいていただければと思います。

ではまた。

Discussion