こんにちは、サイオステクノロジー武井です。今回は、ちょっと難解なOAuthをわかりみ深く説明してみたいと思います。本記事は、私がOAuthを理解するまでの備忘録みたいなものになり、説明が至らないこと多々あると思いますが、生暖かい目で見守って頂けますと幸いです。
全4回シリーズでお届けする予定で、今回は第3回目となります。
- その1:OAuthってなに?
- その2:アクセストークンとリフレッシュトークン
- 今回はこちら → その3:OAuthを認証に使うことの危険性
- その4:stateパラメーターによるCSRF対策
本ブログでは、OAuthを認証に使うことの危険性について説明したいと思います。これはある特定の条件下で発生します。
- インプリシットフローを使っている
- アクセストークンの発行元を検証していない
- プロファイル情報APIによって得たユーザー情報を認証ユーザーとしている
OAuth認証とは?
では実際にやってみましょう。まずはじめに通常のOAuth認証の流れについて説明します。登場人物は以下のとおりです。
Aさん・・・格ゲーXをやる善良な市民です。 | |
ゲームプラットフォーム・・・ゲームを開発するためのプラットフォームです。LINEとかが超有名です。 | |
格ゲーX・・・とあるゲームプラットフォームのもとで開発された善良なスマホ用格闘ゲームです。 | |
ゲーム開発者・・・格ゲーXを開発している善良な開発者です。 |
まず格ゲーXを開発するゲーム開発者は、ゲームプラットフォームに対して、OAuthで接続させてくださいみたいな事前の申請を専用の申請フォームより行います。その申請フォームに必要な項目を入力します。そのときに必ず、ゲームプラットフォームに対して行いたい作業(閲覧や投稿)を申請します。この場合は、アクセスしてきたユーザーのプロファイル情報(ユーザー情報や名前など)を操作できる権限を申請します。
申請が終わると、ゲームプラットフォームは「クライアントID」「クライアントシークレット」を発行します。またゲーム開発者から申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。ここではユーザーの情報を提供するAPIを利用したいので「profile」というサービスを申請しています。
Aさんは自分のスマホ内にインストールされている「格ゲーX」をやるためにユーザー認証をします。「格ゲーX」は対戦記録を保存したりネット上のユーザーと対戦したりするため、ユーザー認証が必要なのです。
格ゲーX内の埋め込みブラウザが起動して、ゲームプラットフォームの認証画面にリダイレクトされます。ちなみにインプリシットフローを使うので、response_typeはtokenです。
認証が完了すると、ゲームプラットフォームはアクセストークンを生成して、事前に登録したリダイレクトURIにのハッシュフラグメントaccess_tokenに付与し、リダイレクトします。格ゲーXはそのリダイレクトURIのハッシュフラグメントからアクセストークンを取り出し、格ゲーXのデータベースに保存します。この時点で格ゲーXはAさんのユーザーIDはわかりませんので、空白です。
先程取得したアクセストークンを使って、ゲームプラットフォームのプロファイル情報APIにアクセスします。
プロファイル情報APIのレスポンスのJSONにuser_idというフィールドがあり、それがユーザーIDになります。ユーザーIDはa-sanなので、格ゲーXにアクセスしてきたユーザーは、a-sanというユーザーIDの人であることがわかりました。なので、格ゲーXのデータベースの中にユーザーIDをa-sanとして書き込みました。これでa-sanが認証できたわけであり、これはOAuth認証になります。
OAuth認証によるなりすましとは?
本章では、先程説明したOAuth認証によって、Aさんの情報が乗っ取られる流れをご説明します。以下が今回の登場人物ですが、「悪いゲーム開発者」と「悪ゲーX」が増えました。
まず悪ゲーXを開発するゲーム開発者は、ゲームプラットフォームに対して、OAuthで接続させてくださいみたいな事前の申請を専用の申請フォームより行います。その申請フォームに必要な項目を入力します。そのときに必ず、ゲームプラットフォームに対して行いたい作業(閲覧や投稿)を申請します。この場合は、アクセスしてきたユーザーのプロファイル情報(ユーザー情報や名前など)を操作できる権限を申請します。
申請が終わると、ゲームプラットフォームは「クライアントID」「クライアントシークレット」を発行します。またゲーム開発者から申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。ここではユーザーの情報を提供するAPIを利用したいので「profile」というサービスを申請しています。
Aさんは「悪ゲーX」が、悪意のあるゲームであり、Aさんになりますことを企んでいるトンデモ野郎が開発したゲームであることを知りません。なので、Aさんは「悪ゲーX」にユーザー認証を要求します。
悪ゲーX内の埋め込みブラウザが起動して、ゲームプラットフォームの認証画面にリダイレクトされます。ちなみにインプリシットフローを使うので、response_typeはtokenです。
認証が完了すると、ゲームプラットフォームはアクセストークンを生成して、事前に登録したリダイレクトURIにのハッシュフラグメントaccess_tokenに付与し、リダイレクトします。悪ゲーXはそのリダイレクトURIのハッシュフラグメントからアクセストークンを取り出し、悪ゲーXのデータベースに保存します。この時点で悪ゲーXはAさんのユーザーIDはわかりませんので、空白です。
先程取得したアクセストークンを使って、ゲームプラットフォームのプロファイル情報APIにアクセスします。
プロファイル情報APIのレスポンスのJSONにuser_idというフィールドがあり、それがユーザーIDになります。ユーザーIDはa-sanなので、格ゲーXにアクセスしてきたユーザーは、a-sanというユーザーIDの人であることがわかりました。なので、悪ゲーXのデータベースの中にユーザーIDをa-sanとして書き込みました。同時に悪ゲーXは、Aさんのユーザー情報をクラウド上のデータベースに保存したとします。
悪ゲーXからAさんのユーザー情報が書き込まれたクラウド上のデータベースの管理者はもちろん悪いゲーム開発者です。なので、悪いゲーム開発者はAさんのアクセストークンを取得できます。
では、Aさんのアクセストークンを盗んだ悪いゲーム開発者がどのようにして、Aさんになりますかを見てみます。悪いゲーム開発者は、 「格ゲーX」をやるためにユーザー認証をします。
格ゲーX内の埋め込みブラウザが起動して、ゲームプラットフォームの認証画面にリダイレクトされます。
認証が完了すると、ゲームプラットフォームはアクセストークンを生成して、事前に登録したリダイレクトURIにのハッシュフラグメントaccess_tokenに付与し、リダイレクトするのですが、ここで悪いゲーム開発者はリダイレクトURIのハッシュフラグメント中のアクセストークンを、先程盗んだAさんのアクセストークンに置き換えてしまいます。置き換える方法はいくらでもあると思います。スマホの中にHTTPのリクエストをフックするツールを自作して入れる方法も一つの手段です。
先程取得したアクセストークンを使って、ゲームプラットフォームのプロファイル情報APIにアクセスします。
プロファイル情報APIのレスポンスのJSONにuser_idというフィールドがあり、それがユーザーIDになります。ユーザーIDはa-sanなので、格ゲーXにアクセスしてきたユーザーは、a-sanというユーザーIDの人であることがわかりました。なので、格ゲーXのデータベースの中にユーザーIDをa-sanとして書き込みました。これでa-sanが認証できたわけであります。見事に悪いゲーム開発者はAさんになりすますことができました。
ということで見事(?)になりすましが成立してしまったわけですが、OAuth認証によるなりすましを防ぐには3つの方法があります。
- アクセストークンの発行元を検証すること
- 認可コードフローを使うこと
- OpenID Connectを使うこと
1つ目の「アクセストークンの発行元を検証すること」という方法について説明します。アクセストークンを発行する際に、アクセストークンの発行先のクライアントIDをゲームプラットフォーム(認可サーバー)の中に記録しておきます。先程の例で言えば「悪ゲーX」のクライアントIDは「ghijklmn」なので、 「悪ゲーX」向けに発行されたアクセストークンは、以下のように発行先のクライアントIDを紐付けてデータベースに保存しておきます。
この状態で、「格ゲーX」からアクセストークンでリソースサーバーにリクエストを送るときに、ともにクライアントIDも送ってもらうよにして、アクセストークンをリクエストするクライアントのクライアントIDと、アクセストークン発行先のクライアントIDが不一致であれば、不正であるとして、そのリクエストを破棄してしまえば、Oauth認証を利用したなりすましは防ぐことができます。
残りの2つのうちの一つ「認可コードフローを使うこと」では、認可コードフローを使えばそもそもアクセストークンの置換ができないので成りすましはできず、OpenID Connectを利用すれば、そもそもOpenID Connectはその仕様にアクセストークンの発行先を検証することが仕様として定められているので成りすましを防げますが、本ブログではその詳細は割愛します。
まとめ
いかがでしょうか?OAuth認証の危険性というものがイメージできたら幸いです。至らぬ説明多々あったかもしれませんが、生暖かい目で見守って頂けたらと思います。
次回は、stateパラメーターを使ったCSRF対策について説明いたします。