🎉

OIDCでSAMLのIdP-initiatedなんとかみたいなことをやりたいみなさん

2022/12/01に公開

ritou です。

Digital Identity技術勉強会 #iddance Advent Calendar 2022 1日目の記事です。

https://qiita.com/advent-calendar/2022/iddance

今日はこの話をします。

https://twitter.com/ritou/status/1588171141503328258

SAMLのIdP-initiated フロー/メソッド/SSO とか呼ばれているやつ

仕様だとこの辺りでしょうか?

http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.html#5.1.4.IdP-Initiated SSO: POST Binding|outline

In addition to supporting the new SP-Initiated web SSO use cases, SAML v2 continues to support the IdP-initiated web SSO use cases originally supported by SAML v1. In an IdP-initiated use case, the identity provider is configured with specialized links that refer to the desired service providers. These links actually refer to the local IdP's Single Sign-On Service and pass parameters to the service identifying the remote SP. So instead of visiting the SP directly, the user accesses the IdP site and clicks on one of the links to gain access to the remote SP. This triggers the creation of a SAML assertion that, in this example, will be transported to the service provider using the HTTP POST binding.

これは何かというと、IdPを起点とした認証フローですね。v1から引き継いだともあります。
ちなみに登場人物ですが、

  • IdP : これはそのまま
  • SP : OIDCで言うとRPです

と言うところです。
話を戻して、これはなんなのかをざっくりまとめると

  1. ユーザーはIdPにログインしている
  2. IdP上でSPのリストみたいなのから利用したいものをクリックする
  3. 同じユーザーで ログインした状態で SPに移動できる!便利やろ!

みたいなことが実現できると言うことですね。

世の中に星の数ほどあると言われるSAMLの解説記事にもよく出てくるので聞いたことがある方も多いでしょう。

で、これを2022年にもなってOIDCでやりたい人がいるようです。
実際、自分も聞かれたことがあるので、その辺りについて少しお話しします。

やっていることは"アサーションを作って送りつける"だけ

プロトコルとしてやっていることは、SPのリンクをクリックしたら

  1. IdPがアサーションを生成
  2. IdPからSPにアサーション(とユーザー)を送りつける
  3. SPはアサーションを検証し、ログインさせる

というものです。公式の図はあんまり直感的ではない気がしますが貼っておきます。

出自 : Figure 14: IdP-Initiated SSO with POST Binding http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.html#5.1.4.IdP-Initiated SSO: POST Binding|outline

具体的なXMLとかは割愛しますが、OIDCでいうと、いきなりOPがIDToken作ってRPに送りつけるようなものです。

CSRFのリスク

IdP -> SP と一方的にリクエストがいくということは、こういうことができるはずです。

  1. 攻撃者がIdPにログインしてIdP Initiatedなフローを開始
  2. IdPが攻撃者のためのアサーションを生成
  3. IdPからSPに移動する際にアサーションを攻撃者が取得
    4. 攻撃者は第3者にアサーションを送りつける
  4. 第3者は 攻撃者のアカウントでログインさせられる

おわかりいただけましたでしょうか?
いわゆる "CSRF" 可能な事案であり、我々 "state警察" の担当であります。
これまでも口酸っぱく「state/nonce/PKCE」などでID連携の一連のフローにおけるセッションの同一性を確認しろと言ってきたじゃないですか。

https://ritou.hatenablog.com/entry/2019/12/02/060000

これをやっていないというか、できないので仕様に書いてあるまんま実装すると、リスクが残るわけです。
日本語ではそんなに見かけませんが英語だと解説記事を書いている人はいます。

https://www.scottbrady91.com/saml/dangers-of-idp-initiated-sso

JAPANの #SAMLai ことSAML大好きニキたちには申し訳ないですが、こんなの普通に使っちゃダメなんじゃねーの と言いたいところでございます。
が、ここではOIDCで安全にこのフローを実現させるにはどうすべきかというのを考えていきましょう。

安全にしつつOIDCでどうやるか

IdPが作るアサーションから始まるフローを安全に実装するためには、こんなことをする必要があるでしょう。

  • SPがアサーションに含まれる何かを使ってCSRFが行われていないことを検証する
  • IdP-initiatedを使わず、SP-initiatedなフローを使って想定するUXを実現する

そもそも、OIDCのフローはRPが起点となっていることはご理解いただけているでしょうか。SAMLでいうところのSP-initiatedの方にあたります。
誰かは私にOIDでもOP-initiatedなフローを作りつつSession Managementと併用してセッションの一致を検証するという提案をしていましたが、どう考えても筋が悪いですね。
なので、後者で進めましょう。

OIDCのAuthZ Code Flowを思い出しましょう。

よくある解説ではRP上でユーザーが〜〜でログインみたいなのをえらんだ所からフローが始まるわけですが、これを

  1. OPがRPのリストをユーザーに提示
  2. ユーザーはRPのリンクをクリック
  3. OPはRPにユーザーを送る(リダイレクト)
  4. RPは認証フローを開始する

という風に変えてみます。

これでどうでしょう。RPが認証リクエストを送る部分からの一連の流れは変えずに、OP上で選択したRPにログインした状態が作れそうです。さらに、RP->OP->RPというOIDC Danceが行われる部分には変更がないので既存のセキュリティ機構を適用できます。RPにリクエストが行った際にログイン中のユーザーを切り替えたりする部分やOPからユーザーを指定する方がいいケースなど、細かいところで考えることはありそうですが一旦置いておきます。

そもそも必要とされているのか?

こーゆーのは社内ポータルからリンク踏んで連携サービスに遷移するみたいなところで使われているようなそうでもないような気がします。C向けだとあまりイメージはつきませんが、フィッシング対策のような感じでIdPだけをブックマークしておけばいいみたいな使われ方はあり得るのかなと思ったりします。

標準化するとしたら

例えばこのエンドポイントのURLを RPがOPに対して op_initiated_login_redirect_uri みたいな名前でRPからOPに登録できるようにして、あとは必要ならOPが付与するパラメータ(login_hintとかはありかも)を決めてやれば、より拡張仕様っぽく扱えるかもしれません。

本気で必要としている人がいるなら提案したらいいと思います。

まとめ

  • SAML の IdP-initiated 相当のフローをOIDCで実現するための話
  • SAML の IdP-initiated には CSRFのリスクがあるので SP-initiated 相当を使って想定するUXを実現する方針の方が筋が良さそう
  • OIDCのシーケンスで説明した

以上です。

明日は @kg0r0 さんが何か書いてくれそうです。

https://qiita.com/advent-calendar/2022/iddance

ではまた。

Discussion