こんにちは、サイオステクノロジー 武井です。
※本記事の英語版はこちら(Click here to read in English)
今回は、OAuthを認証に利用することの危うさについて書きたいと思います。参考にさせていただきましたのは以下の記事になります。
https://www.thread-safe.com/2012/01/problem-with-oauth-for-authentication.html
この記事では、OAuth2.0のImplicit Flowを使って認証を行った場合、悪意のあるユーザーが、簡単に他の誰かになりすますことができてしまうということが書いてあります。
今回、これを私なりにわかりやすく解説を試みてみたいと思います。
OAuth認証とは?
本来、OAuthは認証につかうプロトコルではありません。APIなどの認可(このユーザーに対しては、ユーザー情報を読み取るためのAPIしか使えませんよー的な制限を設けることです)に使うプロトコルです。この場合は、OpenID Connect(詳細は、多分わかりやすいOpenID Connect参照)が適切なのですが、ある手法を使うと、OAuthを認証にも使うことができてしまいます。本章では、このことについて、図を交えながら説明します。
まず、今回の説明の中で登場する人物やサービスをご紹介致します。
これより、OAuthの認証の仕組みを説明します。SNS2はImplicit Flowを使っているものとします。まずSNS1を運営するシステム管理者は、SNS2に対して、OAuthで接続させてくださいみたいな事前の申請をします。これは、SNS2のサービスに申請専用の画面が用意されていることが多いです。その申請フォームに必要な項目を入力します。そのときに必ず、SNS2に対して行いたい作業(閲覧や投稿)を申請します。この場合は、アクセスしてきたユーザーのプロファイル情報(ユーザー情報や名前など)を操作できる権限を申請します。
申請が終わると、SNS2は「クライアントID」「クライアントシークレット」をSNS1システム管理者に発行します。またSNS1から申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。SNS2、SNS1は、そのクライアントID、クライアントシークレット、利用サービスを自身のデータベースに保存します。ここまででSNS1システム管理者の仕事は終わりです。この作業はOAuth利用時にシステム管理者が最初の一度だけ行えばよい作業です。
Aさんは、SNS1にアクセスします。
SNS1は、SNS1のログイン画面(Authorization Endpoint)へAさんをリダイレクトします。すると、SNS2のログイン画面が出てくるので、AさんはSNS2にログインするためのIDとパスワードを入力します。
ログインすると、SNS2は、SNS1と連携してもよいかどうかの確認画面を表示します。
Implicit Flowですので、事前にSNS2側でシステム管理者が登録したコールバックURL(https://sns1.example.com/cb.php)のハッシュフラグメントとして、アクセストークンが付与され、Aさんに経由で、SNS1にリダイレクトされます。SNS1はハッシュフラグメントを解析して、アクセストークンを取り出して、SNS1のデータストアに格納します。
SNS1は取得したアクセストークンを使って、SNS2のプロファイルAPIにアクセスして、AさんのIDを取得します。
SNS1は、プロファイルAPIのレスポンスにAさんのIDが含まれていることから、SNS1にアクセスしてきたユーザーはAさんと判断し、Aさんの情報を画面に表示します。これがOAuth認証です。一見すると、なんの問題もないように見えますが、これは、SNS1を含む全てのサイトが「いいサイト」だった場合です。
車が通れるほどのドデカイセキュリティホールができてしまう場合
本章では、OAuthを認証に使った場合に、どのようなセキュリティリスクがあるかをご説明します。ここで新たな登場人物をご紹介致します。
悪SNS・・・ユーザーAさんの情報を不正に取得し、Aさんになりすますことを企む悪い業者が運営する悪いSNSです。 | |
悪SNSシステム管理者・・・悪SNSのシステム管理者です。 |
では、悪SNSが、いかにして、Aさんになりすますかをご説明していきたいと思います。
SNS1のときと同様、まず悪SNSを運営するシステム管理者は、SNS2に対して、OAuthで接続させてくださいみたいな事前の申請をします。アクセスしてきたユーザーのプロファイル情報(ユーザー情報や名前など)を操作できる権限を申請します。
申請が終わると、SNS2は「クライアントID」「クライアントシークレット」を悪システム管理者に発行します。これもSNS1のときと同様ですね。
Aさんは、悪SNSにアクセスします。しかし、Aさんは悪SNSが、悪意のあるSNSであり、Aさんになりますことを企んでいるトンデモ野郎であることを知りません。
悪SNSは、SNS1のログイン画面(Authorization Endpoint)へAさんをリダイレクトします。すると、SNS2のログイン画面が出てくるので、AさんはSNS2にログインするためのIDとパスワードを入力します。
Implicit Flowですので、事前にSNS2側でシステム管理者が登録したコールバックURL(https://sns1.example.com/cb.php)のハッシュフラグメントとして、アクセストークンが付与され、Aさんに経由で、悪SNSにリダイレクトされます。悪SNSはハッシュフラグメントを解析して、アクセストークンを取り出して、悪SNSのデータストアに格納します。
悪SNSは取得したアクセストークンを使って、SNS2のプロファイルAPIにアクセスして、AさんのIDを取得します。
悪SNSは、プロファイルAPIのレスポンスにAさんのIDが含まれていることから、悪SNSにアクセスしてきたユーザーはAさんと判断し、Aさんの情報を画面に表示します。
これまでの過程で、悪SNSのシステム管理者はAさんのアクセストークンを取得しました。これより、悪SNSのシステム管理者は、Aさんのアクセストークンを悪用して、SNS1のサービスにてAさんになりすまします。
悪SNSシステム管理者は、SNS1にアクセスします。
SNS1は、SNS1のログイン画面(Authorization Endpoint)へ悪SNSシステム管理者をリダイレクトします。すると、SNS2のログイン画面が出てくるので、悪SNSシステム管理者はSNS2にログインするためのIDとパスワードを入力します。
ログインすると、SNS2は、SNS1と連携してもよいかどうかの確認画面を表示します。
Implicit Flowですので、事前にSNS2側でシステム管理者が登録したコールバックURL(https://sns1.example.com/cb.php)のハッシュフラグメントとして、アクセストークンが付与され、悪システム管理者経由で、SNS1にリダイレクトされます。しかし、ここがキモなのですが、悪SNSシステム管理者は、リダイレクトURLを書き換えて、つまり、悪SNSシステム管理者のブラウザ自身へのHTTPレスポンスに含まれるLocationヘッダに記載されたURLを書き換えて、ハッシュフラグメントの部分を、先程不正に取得したAさんのアクセストークンに置き換えます。
すると、SNS1は悪SNSシステム管理者を経由してリダイレクトされてきたアクセストークンを受け取り、SNS2に対してそのアクセストークンでプロファイルAPIを発行します。すると、、、
なんと、Aさんのプロフィール情報が返ってきてしまいました。これで、Aさんになりすましてログインできることになってしまいます。
対策について
対策は主に2つあると思います。
一つは、SNS1はそのアクセストークンの発行元を検証することです。今回の大きな原因は、SNS1が、アクセストークンをのべつ幕なしにすべて受け入れていることにあります。SNS1がアクセストークンを発行する際、このアクセストークンがどのクライアント宛に発行されたものかを記録し、アクセストークの検証をする際、合わせてその発行元も確認することです。例えば、SNS1はSNS2にアクセストークンを発行する際、このアクセストークンがSNS1宛に発行されたものだという情報をどこかに持っておきます。データベースでもファイルでもなんでも構いません。そして、SNS1がアクセストークンをSNS2に送ってきた場合、SNS1から発行されたものでなければ、それをSNS1側で拒否をします。そうすれば、この攻撃を防ぐことができます。
もう一つは、OpenID Connectを導入することです。これが一番、確実な対策かと思います。もともと、OAuthは認証のために使うプロトコルではありませんし。