ものがたり(旧)

atsushieno.hatenablog.com に続く

OAuthのセッション固定攻撃について(翻訳)

Explaining the OAuth Session Fixation Attackという文章が興味深いものだったので翻訳してみた。何か解決策を思いついた人はOAuthのメーリングリストに送ってあげると良いと思う。って僕は参加してもいないのだけど。あと誤訳とかはコメントしてもらえれば対応します。ワタクシ実のところOAuthなんて使ったこともなかったりして。

(原文はリンク先にもある通り、Eran Hammer-Lahav氏からcc-by 3.0 usで提供されている。)

追記: 日本でもニュースになっていた: http://www.atmarkit.co.jp/news/200904/23/oauth.html

追記2: 元記事の画像がアップデートされていたので、追従して更新

以下翻訳:

先週、われわれが発見して対応したOAuthのプロトコルセキュリティ問題には語るべきことが多くある。詳しくはすぐ後に書くけど、この話は、今回の脆弱性再現の実際の技術的な詳細よりも重要で興味深いものだ。

全ての関係者にとって、明示的な意思決定モデルが無いオープンなコミュニティにおける、オープン仕様におけるセキュリティホールの対応は(ソフトウェアバグとは異なり)、初めての経験だった。そもそもどこから始めれば良いのだろうか?

とりあえず、皆さんは技術的な詳細について知りたいことだろう。

もしあなたがこれを読んでいるなら、あなたにはOAuthがどういう仕事をするものか、基本的な理解があると考えて良いだろう。でなければ、わたしのOAuthの入門者ガイド を、最初の2部だけでもいいから読んできてほしい。

最初の部では、一般的な概要を、次の部ではユーザ・ワークフローを、説明してある。今回、重要なのはそのワークフローだ。このガイドの残りの部分はセキュリティ署名 について扱っていて、それらの投稿は、驚くべきことに、この攻撃とは無関係だ。

まず、この攻撃が何でないかについて書いておこう。これはユーザ名やパスワードを盗み出そうというものではない。これはトークンとかシークレットとかコンシューマ・キーを盗みだそうというものでもない。実際、OAuthの署名ワークフローで関係する部分は何もない。これは暗号化に関する攻撃ではない。そしてこれはユーザが特定のアプリケーションに対してアクセスを許可するという原則を攻撃するものではない。これらは全て無傷のままだ。

わたしがずるずると話を引き延ばしているのではないかと思うかもしれないが、これは何が壊れていて何がまだ安全であるか(これはつまり、既知の脆弱性再現はないということなのだけど)を理解するために決定的に重要なことだ。この情報が公になっても、OAuthを利用したサービスは存続し稼働し続けるだろうし、それらを利用することについて安全だと考えてもらいたい、という意味で重要なのだ。

このセキュリティ問題の正確な詳細を理解するのは、脆弱性の再現を防ぎ仕様を修正するために重要なことだ。しかしそれと同様に、何がなお上手く機能し信用に値するものであるか知っておくことも重要だ。

たとえば、多くのアプリケーションが、ユーザ承認を要しない二本足のリクエストにOAuthを使っているが、これらは今回の問題に影響を受けない。コミュニティの努力によって、われわれの作業の大部分は、そのプロトコルの採用とサポートを実現することだった。われわれは問題を全て開示する必要があるし、このプロトコルの評価が誤解や推測によって打撃を受けないようにする必要もある。

今回発見された問題は、ソーシャルエンジニアリング攻撃によって権限を獲得するセッション固定攻撃だ。この基本的なアイディアは、攻撃者がOAuth APIを使ったアプリケーション(コンシューマ)を仕掛けて、誰かのリソースに(アクセストークンを用いて)アクセスするというものだ。攻撃者はアクセストークンそのものを取得することなく、そのアプリケーションを利用する能力を有しているだけだ。しかしそのアプリケーションにはアクセストークンがあるので、攻撃者は被害者のリソースにアクセスするためにそのアプリケーションを使うことができてしまう。

OAuthの承認ワークフローは3つのステップからなる:

  1. ユーザがアプリケーションに対して彼のリソースにアクセスすることを要求することで、このフローが開始される。これがトリガーとなって、アプリケーションがプロバイダからのリクエストークンを要求して、ユーザをプロバイダにリダイレクトして、承認してもらうことになる。
  2. ユーザはプロバイダにサインインして、そのアプリケーションへのアクセスを許可する。もしユーザがフィッシング攻撃について十分に教育を受けていれば、彼はここでパスワードを本当にプロバイダにのみ与えて第三者には与えていないことを確認できる。
  3. アクセスを許可したら、ユーザはアプリケーションにリダイレクトで戻されて、フローを完了して、このアプリケーションに彼のデータへのアクセスを有効にすることができている。

これらの3つのステップを結びつける唯一の要素は、承認URIとコールバックURIにおけるリクエストークンだ。どちらについても、その呼び出しは署名されているものではなく、簡単に推測できるものだ。問題は、コンシューマに対してもプロバイダに対しても、これらの3つのステップが同一のユーザによって行われているという保証が無い、ということだ。アプリケーションは、クッキーや他のセッション管理ツールによって、最初のステップと最後のステップが同一のユーザによって行われていることを確認できるが、真ん中のステップが、最初または最後のステップと同一のものであるかどうかは確認できない。

通常のOAuthワークフローは以下のようになる:

攻撃者は、そのプロセスを、アプリケーションのアカウントを一つ使ってフローを開始して、そのアプリケーションがリクエストークンを取得してプロバイダにリダイレクトするフローの開始を、問い合わせるところから始める。この時点で、攻撃者はこれを止めておいて、そのリダイレクトURIに含まれるリクエストークンを記録しておく。攻撃者はこのコールバックを変更したりするかもしれないが、それについては後で触れる。

そうしたら、攻撃者はソーシャルエンジニアリングを用いて、被害者にそのリンク(リダイレクトのための承認URI)を辿らせる。これは、そのアプリケーションのレビューのブログ記事などで、人々に試してみるように言えば、簡単にできる。誰かがそのリンクをクリックしたら、それらはアクセスを承認するようプロバイダに送信される。

被害者は、これこそが彼のやりたかったことなので、彼自身がアプリケーションそのものを開始すべきだったと気づくこともなく、続けてプロバイダにサインインするだろう。われわれが人々にフィッシング攻撃を十分に確認するよう指導しているおかげで、十分に教育を受けたユーザであっても、彼は自分が正しい場所にいるのだと確認することだろう。

そしてプロバイダは正しくそのアプリケーションを明示した上でユーザにアクセス許可を尋ねる。これでユーザは、何もかもが上手くいっていることを確認し、ますます自信のレベルを高めるだろう。正しいプロバイダと正しいアプリケーションである。しかし、ユーザがアクセスを許可するなり、攻撃者は(そのアプリケーションがどう動いているかを学習したり、リダイレクトURIに含まれるコールバックパラメータから判断するなどして)コールバックURIを作り出して、そのアプリケーションに戻すのである。

この時点で、攻撃者のアプリケーションのアカウントは有効かつ承認された被害者のアクセストークンと結びつけられている。思い出してほしい。攻撃者はアクセストークンやシークレットの類は一切有していない。リクエストークンのシークレットを取得してもいないし、OAuthリクエストを署名しているわけでもない。攻撃者が保有しているのは、その被害者のリソースと結びつけられたアプリケーションのアカウントのみなのである

この攻撃のフローは次のようになる(一部簡略化してある):

コールバックについても軽く触れておこう。OAuthには、オプションでコールバック・パラメータが含まれていて、コンシューマがユーザを承認プロバイダに送り出す時にコールバックを設定できるようにしている。プロバイダはコールバックサポートを以下のような方法で実装することが出来る:

  1. どんなコールバックも許可
  2. 登録済みの(そして確認済みの)ドメインならどんなコールバックも許可
  3. 登録済みの静的なコールバックのみ許可し、パラメータは無視
  4. コールバックは一切不許可、ユーザの手作業によるアクションを要求

もしプロバイダが最初の選択肢を用いるなら、攻撃者は単にコールバックを変更することができる。承認のステップは署名されていないので、攻撃者はコールバックパラメータを変更することが出来るのだということを思い出してほしい。これによって、ユーザを攻撃者がコントロールするページに送り出して、いつそのアプリケーションにさらに戻して処理を続行させることができるかを、攻撃者に知らせているのである。

もしプロバイダが第二あるいは第三の選択肢を用いるなら、これは攻撃者と被害者のどちらがアプリケーションに最初に制御を戻すかという競争状態になる。攻撃者は、いつユーザがアクセスを許可するか正確に知ることはできないので、アクセスが成功するまで何度か試行することになるだろう。そういったブルートフォース攻撃を全トランザクション無効化によって適切に処理するかどうかは、コンシューマ・アプリケーション次第である。しかしこれは、アプリケーションがそれらのケースを検出し処理することを要求することになる。

第二の選択肢について考慮すべきもうひとつの事項は、ドメインには与えられたパラメータによってリクエストをリダイレクトするオープン・リダイレクタになっているものもあるということだ。たとえば、人々が広告や外部リンクをクリックした回数をカウントする方法はこれである。攻撃者はそういったリダイレクタを使ってコールバックURIを登録済みドメインのものに設定し、攻撃者がコントロールするサイトに送り出すようにすることも出来るのである。

もし最後の選択肢を用いるなら、これは第二、第三の選択肢に類似するが、競争状態はよりずっと簡単になる。というのは、被害者はもはやアプリケーションに(自動的なリダイレクトによって)戻されることが無いからだ。また、手作業による「終わったらここを押してください」という場合には、アプリケーションはユーザの失敗を予期して複数回の試行を可能にする傾向がある。そのような場合は、アプリケーションは単にリクエストークンとアクセストークンを交換して、アクセスがユーザによって承認されるまで失敗しつづけるかもしれない。もしプロバイダが最初の失敗でトランザクションを無効化することがなければ、露出が増大していることになる。

さて、この攻撃について、そしてコールバックについて、説明してきたが、話はこれだけではない。

もし攻撃者が先にアプリケーションに戻らなかった、あるいはアクセスが許可されたことを知ることがなかったとしても、このアプリケーションには致命的な設計上の欠陥があるかもしれない。アプリケーションによっては、そのアクセストークンにかかるフローを開始したユーザのアイデンティティを、ローカルアカウントと結びつけて使用する。これはつまり、もし被害者がそのアプリケーションに先に戻ったとしても、そのアプリケーションはなお彼のアクセストークンを攻撃者のアカウントと関連づけることになる。というのは、このアプリケーションはデータベース中のリクエストークンをルックアップして、その情報を用いて処理を続行するかもしれないからだ。

これらの情報を総合すると、たとえアプリケーションが:

  • 開始したユーザではなくコールバックから戻ってきたユーザのアカウントを使うことを確実にして、
  • プロセスを開始したユーザが、完了したユーザと一致することをチェックして、
  • 登録時に改竄できないコールバックを使用した

としても、それはなおも、アクセスを承認する被害者と彼のブラウザによるリダイレクト呼び出しの間に割り込もうとするタイミング攻撃の危機には晒されている。承認を与えた人物が、戻ってきた人物と同一であるかどうかを、簡単に知る術はない。今のところ、何も存在しないのだ。

以上のような話だ。

解決策について皆さんが考え始める前に。コミュニティ・メンバの数人が、サービスプロバイダによって、戻されるコールバックURIの中に予期できないコールバックパラメータを追加するという案について考えている。コンシューマは、リクエストークンをアクセストークンと交換するために、この新しいパラメータを知る必要がある。それによって、本当のユーザがアプリケーションに戻ってフローを完了させたことを確認する、というものだ。

われわれは明日戻ってきて、この問題を修正してプロトコルのリビジョンを上げるために考え合うつもりだ。とりあえず、この問題について考えたり、OAuthのメーリングリストでの相談に参加したり、同僚と相談したり、あるいは自分の考えをブログに書いたりしてほしい。

皆さんも気をつけて。

Brian Eaton、Dirk Balfanz、Chris Messinaがこの投稿に有益なフィードバックをくれた。