2.2k SHARES Facebook Twitter

implicit grant flow を認証に使うと、車が通れる程どてかいセキュリティ・ホールが開くよ、と言う、ジョン・ブラッドレー氏[1]による良記事。コメントも読み応えあります。ちょっとチェックした見たところは、全滅。RP側を治さなきゃいけないから、とっとと公開アナウンスしたほうが良いのでしょうね。いちいちコンタクトしてられないし。 OAuth 2.0 のを認証に使うと、車が通れる程どてかいセキュリティ・ホールが開くよ、と言う、ジョン・ブラッドレー氏[1]による良記事。コメントも読み応えあります。ちょっとチェックした見たところは、全滅。RP側を治さなきゃいけないから、とっとと公開アナウンスしたほうが良いのでしょうね。いちいちコンタクトしてられないし。 Facebook や、その他OAuthログインしているサイトはみんなチェク！

The problem with OAuth for Authentication. | Thread Safe In some of the feedback I have gotten on the openID Connect spec, the statement is made that Connect is too complicated. That OAuth 2.0 is all you need to do authentication. Many point to Identity Pro…

英語読みたくないという人のために簡単に解説すると…

OAuth 2.0 の implicit flow を使って「認証」をしようとすると、とっても大きな穴が開きます。

カット＆ペーストアタックが可能だからです。

OAuth 認証？は、図１のような流れになります。

一見、問題なさそうに見えます。しかし、それはすべてのサイトが「良いサイト」ならばです。

Site_A が実は悪いサイトだったとしましょう。すると、Site_A は、このユーザになり変わる access_token をまんまと入手してしまったことになります。

Site_A は、以後、このユーザになりすまして、任意の「OAuth 認証？」をやっているサイトにログインすることができます。

非技術者のためのOAuth認証(?)とOpenIDの違い入門 にも書きましたが、宛先の書いてない合鍵渡しちゃって、それを持っている人は誰でも私の分身ですと言っているわけですから当たり前ですね。

具体的に書くと、

Site_A はブラウザ(User Agent) UAを使って、Site_B に行ってログインしようとします。すると、上記と同じ手続で Site_B は「認証」をしようとします。UAは Site_B用の、攻撃者用のアクセストークン access_token_B をOAuth の Authorization Endpoint (Authz) からもらいますが、これをSite_Bには渡さずに、さっき取得した、ユーザ（＝被害者）のアクセストークン access_token_A を代わりに渡します。Site_Bがこのトークンが本当はSite_A用のものだと認識する手段はありません。なので、自分向けのものとして受け取ってしまいます。そして、Site_Bは GraphAPI に access_token を投げて、被害者のemail や user_id を取得しようとします。GraphAPI が、このSite_A用のトークンを送ってきているのがSite_Bだということを認識する手段もありません。したがって、GraphAPIは、Site_Aがリクエストしてきたのと同様に、被害者のemailやuser_idを送り返してしまいます。結果、Site_Bは、攻撃者を被害者としてログインさせてしまいます[5]。この流れが図２です。

これは、OAuth の state パラメータを使って XSRF 対策をしていても防げません。つまり、OAuth 2.0 の Client は、そのClient (サイト)にログインしたすべての人になりすまして、任意の他のOAuth 対応サイトにログインできるのです。

これは、OAuth の問題ではありません。

OAuth は Authorization Delegation Protocol = 認可をデリゲーションするためのプロトコルであって、ユーザ認証のためのプロトコルではないからです[5]。はっきり言って、楽ちんだからといって、それを単体で認証の代わりに使っている方が悪い。

実は、Facebook もこのことは気づいていて、signed_request というAPIを持っています。これはほとんど OpenID Connect と同じです[2]。Facebook でログインするためには、こちらを使わなければいけないのです。scope=signed_request ってやるんですよ。でも、使っている人、どれくらい居ますか？やってます？ほとんどは、access token を取得するための client side flow (Facebook のデフォルト） を認証の代わりにつかっちゃってますよね？！[7]

Google の Identity Service の責任者の Eric Sachs 氏の、John の blog に寄せられた投稿も、このことの重要性を指摘しています。

OpenID Connect ではなく、単なるOAuth を認証に使っているIdPが巨大なセキュリティホールを生み出しているということに関する、ジョン・ブラッドレー氏によるすばらしい記事。これは、至るところで繰り返し言い続けなければならない。IdPに対しては、パートナーに対してセキュリティ上の問題を生んでいるということを理解してもらうために。RPsには、数行のコードをケチったために、自らのセキュリティを台無しにしているということに気付いてもらうために。数年前、Googleが現在のOAuthにあたる独自API「AuthSub」を公開したときは、まさにこの理由のために「認証に使ってはいけない」旨を、ドキュメントの最後に大きく掲載していた。[3]

問題の原因は、access_token の audience は resource endpoint であるのに対して、認証に使うトークンの audience は client でなければいけないというところにあります。だから、OpenID Connect では、client を audience にした id_token という、access_token とは別のトークンを発行しているのです。Facebook の signed_request も同じです。

ちゃんと治してくださいね、皆さん。治すってことは、OpenID Connect 対応するってことですよ！

大した工数じゃないんだから。数行を惜しんでユーザを危険に晒す[4][8]のは、ぜひやめていただきたいところです。

[1] John Bradley. アメリカ政府の ICAMの中の人で、IMI, OpenID, SAML のプロファイルを書いている。OpenID Foundation 理事。Kantara Initiative リーダーシップカウンシル議長。今回の記事は、OAuth 認証のプロファイルを書こうとして、「だめだこりゃ」ということらしい。

[2] signed_request は、Facebook 独自の署名方式をとっているのに対して、OpenID Connect は IETF JOSE WG で標準化されているJWSを利用しています。また、signed_request では、access_token 自体をsigned_request の中に入れていますが、OpenID Connect では、他のOAuth 2.0 サイトとの互換性を考慮して、外出しにしています。

[3] 原文: Great post by John Bradley on the huge security hole many IDPs have created by using plain OAuth, instead of OpenIDConnect, for authentication. We need to keep hammering away on this point both so IDPs realize the security problems they are creating for their partners, and to get RPs to realize how easily they can compromise their own security just because of the lack of a few additional lines of code. Years ago when Google first launched its proprietary equivalent of OAuth, called AuthSub, we had a big section at the bottom warning people not to use it for authentication for exactly this reason. (source: https://plus.google.com/u/0/102425765611793764729/posts/UKcZQzuvosQ )

[4] 乗っ取られたとしても何も起きないのならば良いのですが、ユーザの個人情報を貯めてたりしたら、当然個人情報漏えい事件になりますよね。

[5] 太字部分、2/3追記。

[6] （2/3追記）攻撃者であるSite_Aが、自分あての access_token を他の人に渡すのは、Site_Aが自分でアクセスした結果を渡すのと得られる結果は同じです。したがって、GraphAPI/Resourceの提供者の立場からしたら、Site_A用の access_token を Site_Bが使うことは、別にリスクが増加していることにはなりません。

[7] (2/17追記）自前でコードを書くのではなく、Facebook の Javascript SDK を使う場合、signed_request を使うように成っている。さらに、昨年の７月からは、ドキュメントと違い、signed request には access_token ではなく code が入るように変わっていた。多くの人が JS SDK を使って、自前でコードを書いていないとすれば、FBに関しては被害はそれほどでもないということになる。ただし、iOS SDK や Android SDK はこのような対策が取られていないので、危ない。これは、 @nov が apple にインシデントレポートを上げている。

[8] (2/17追記) そもそも、Javascript などクライアントデバイス上のものに渡した access_token を、サーバサイドに渡すのはありえない。渡さなければ、たかだか攻撃者のデバイス上にある情報か、プラットフォーム側にあって、もともと攻撃者がアクセス認可をうけていたデータしか取れないはずだから問題は無いはずだという指摘もある。これはそうあって欲しいと願うのだが、たとえば Facebook の開発者向け資料 (https://developers.facebook.com/docs/authentication/) などでは、サーバに送るように図が書いてある。

いいね: いいね 読み込み中...