こんにちは、サイオステクノロジー武井です。今回は、ちょっと難解なOAuthをわかりみ深く説明してみたいと思います。本記事は、私がOAuthを理解するまでの備忘録みたいなものになり、説明が至らないこと多々あると思いますが、生暖かい目で見守って頂けますと幸いです。
全4回シリーズでお届けする予定で、今回は第1回目となります。
- 今回はこちら → その1:OAuthってなに?
- その2:アクセストークンとリフレッシュトークン
- その3:OAuthを認証に使うことの危険性
- その4:stateパラメーターによるCSRF対策
OAuthとは?
よく巷でOAuthって聞きますよね。その特徴は以下の通りとなります。
- 従来のID・パスワードベースとは異なるトークンベースの認証
- トークンには限られた権限だけを与えられているので、万が一トークンが盗まれても被害が少ない
- 認可コードフローなどによりトークンを安全に取得
- トークンには有効期限が設定されており、万が一トークンを盗まれても有効期限過ぎると使えないので、セキュア
これらの4つの特徴を一つずつ紐解くことでOAuthとは何者かを説明していきたいと思います。
OAuthをざっくり説明
本章では、OAuthのイメージをざっくり説明します。先程あげた4つの特徴のうち、以下の2つ(1と2)を説明致します。
- 今回はこちら → 従来のID・パスワードベースとは異なるトークンベースの認証
- 今回はこちら → トークンには限られた権限だけを与えられているので、万が一トークンが盗まれても被害が少ない
- 認可コードフローなどによりトークンを安全に取得
- トークンには有効期限が設定されており、万が一トークンを盗まれても有効期限過ぎると使えないので、セキュア
新しいテクノロジーを理解しようとするときは、それに置き換わる従来のテクノロジーとの比較をすると、分かりやすいです。ということで以下の2つの認証方式を比較します。
ID・パスワード認証 |
従来のIDとパスワードによる認証です。GoogleやOffice365等の外部サービスのAPIの呼び出しに長いこと使われてきました。 |
OAuth |
今回の肝であるトークンベースのイカした認証方式です。最近はもっぱらこれ。 |
以下のユースケースを考えてみます。Twitterにつぶやくと、自動的にそのつぶやきがFacebookにも反映される仕組みです。
ID・パスワード認証の場合
それでは先のユースケースをID・パスワード認証の場合どのように行われるかを見ていきます。
TwitterのシステムにAさんのFacebookのユーザーID、パスワードを登録します。
Aさんがつぶやきます。
Twitterのシステムに登録されているFacebookのユーザーID・パスワードを元にFacebookに接続します。
TwitterがユーザーAさんの代わりにFacebookに投稿します。
このしくみは、大きな弱点があります。TwitterにはAさんのFacebookのユーザーID・パスワードが登録されています。Twitterを運営している人に悪い人がいて、その人がAさんのFacebookのID・パスワードで勝手にFacebookに接続できてしまいます。投稿の削除やアカウントの削除ができてしまいます。これを解決するのはOAuthです。OAuthは、Twitter側にFacebookのユーザーID・パスワードを登録することなく、Facebookにアクセスします。
OAuthの場合
次にOAuthの場合を考えてみます。
OAuthの認証の仕組みを説明します。まずTiwtterを運営するシステム管理者は、Facebookに対して、Oauthで接続させてくださいみたいな事前の申請をします。これは、Facebookなどのサービスに申請専用の画面が用意されています。その申請フォームに必要な項目を入力します。そのときに必ず、Facebookに対して行いたい作業(閲覧や投稿)を申請します。
申請が終わると、Facebookは「クライアントID」「クライアントシークレット」をTwitterシステム管理者に発行します。またTwitterから申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。Facebook、Twitterは、そのクライアントID、クライアントシークレット、利用サービスを自身のデータベースに保存します。ここまででTwitterシステム管理者の仕事は終わりです。この作業はOAuth利用時にシステム管理者が最初の一度だけ行えばよい作業です。
ここからは、利用者であるAさんがTiwitter→Facebook連携を行います。まず、Aさんは、Twitterにアクセスして、Tiwitter→Facebook連携の設定画面にアクセスします。
すると、Aさんは、Facebookにリダイレクトされて、Facebookのログイン画面が表示されます。このリダイレクトの際、URLのクエリパラメータにclient_idを付与します。次の工程でこれを使います。
ログインすると、Facebookは、Twitterと連携してもよいかどうかの確認画面を表示します。Facebookは、多分、いろんなサービスとの連携を許可していると思いますが、その多数あるサービスの中から、Twitterとの連携がOKかどうかの画面を表示できたのは、先程リダイレクトの際にクエリパラメータに渡されたクライアントIDから判断しています。
Aさんが先程の画面で「OK」をクリックすると、Twitterにリダイレクトされます。この際、認可コードというものが発行されます。リダイレクトされたときのURLのクエリパラメータにcode=…と言うかたちで、Twitterに渡されます。これは、後にTwitterがFacebookにアクセストークンを要求するための、非常に有効期限の短い通行手形のようなものです。Facebookは認可コード発行と同時に、認可コードを自身のデータベースに保存します。
TwitterはFacebookにアクセストークンを取得しに行きます。その際、先程発行された認可コードを渡します。
認可コードを要求されたFacebookは、自身が発行してデータベースに登録した認可コードとその有効期限を確認し、問題がなければ、Twitterにアクセストークンを返します。アクセストークンを発行したFacebook、アクセストークンを受け取ったTwitterの両方は、ユーザーIDとアクセストークンが紐付いた情報をデータベースに登録します。
Aさんは、Twitterにつぶやきます。
TwitterはAさんのつぶやきを画面に表示します。それと同時に、Facebookにも同様の投稿を行います。この際、Facebookには、投稿の内容と同時に、アクセストークンを渡します。アクセストークンは、Twitterへのログインユーザーをキーにデータベースから取得します。
アクセストークンを受け取ったFacebookは、データベースからアクセストークンを検索して、もし見つかったら、それに紐づくユーザーIDで、Facebookに投稿します。これで、連携完了です。
Twitterを運営する人の中に悪い人がいたとします。その悪い人がAさんのアカウントを悪用してFacebookのアカウントを削除してやろうと考えました。
でもそんな事できません。せいぜい以下みたいに変なことをつぶやくくらいです。このアクセストークンには、「利用サービス」に登録されていること(この場合はpostとview)しかできないからです。そういう風にFacebook側で制御をかけているのです。
これがOAuthの最大のメリットです。ID・パスワード認証をそのまま他のサービスに渡すと、どのように使われるかわかりません。OAuthのトークンであれば、被害を最小限に済ませることができます。
でもどうして認可コードをわざわざ使ってトークンをやり取りするのでしょうか?手順が複雑になるばかりですし、直接アクセストークンを受け取ったほうがとても便利で楽かもしれませんが、それではちょっと都合の悪い理由があります。それをこれから説明します。
先の「手順その6」から説明します。「手順その6」はこんな感じでした。
では、これを認可コードを受け渡すのではなく直接アクセストークンを受け渡すようにしてみてはどうでしょうか?以下の図のようになります。一見、問題なさそうなのですが、アクセストークンが直接インターネットを流れるので、ハッカーがそれを奪う機会が多くなります。
じゃぁ認可コードでも同じじゃんって感じです。が、認可コードはアクセストークンにくらべて極端に短命です。OAuthの仕様では有効期限10分が推奨です。なので認可コードを奪われたとしても、あまりにも短命なので、それを使って悪いことしようとしたときには期限切れて使えないということになります。
ということで今回の章では以下の2つについて説明しました。
- 今回はこちら → 従来のID・パスワードベースとは異なるトークンベースの認証
- 今回はこちら → トークンには限られた権限だけを与えられているので、万が一トークンが盗まれても被害が少ない
- 認可コードフローやImplicitフローによりトークンを安全に取得
- トークンには有効期限が設定されており、万が一トークンを盗まれても有効期限過ぎると使えないので、セキュア
OAuthの登場人物
本章では、OAuthの登場人物を整理します。
認可サーバー |
リソースオーナーを認証し、アクセストークンを発行するサーバーです。 |
リソースサーバー |
アクセストークンによる保護対象リソースを格納・提供するサーバーです。 |
クライアント |
リソースサーバーにアクセスして、アクセストークンを取得するクライアントです。 |
リソースオーナー |
リソースサーバーで提供されるリソースの持ち主です。 |
文章だけではイメージが湧きにくいと思うので、先程の「OAuthをざっくり説明」の章で紹介した図に当てはめてみるとこんな感じになります。
ちょっとここからの説明の都合上、「OAuthをざっくり説明」で説明した流れを1枚の絵にまとめたのが以下になります。
先程の図で認可サーバーとリソースサーバーが一緒になっていました。Facebookが認可もリソースの提供もやっていたのですが、実はこれが別々になるケースが最近はよくあります。その時の流れは以下の通りとなります。認可サーバーからアクセストークンをもらい、認可サーバーとは別のリソースサーバーにアクセストークンを渡して、リソースを取得しています。
どうして認可サーバーとリソースサーバーが分かれることがあるのでしょうか?それは、最近「IDaaS」というのがはやっているからです。IDaaSの特徴は以下の通りとなります。
- 認証・認可のみに特化したSaaS型のマネージドサービス
- ワンタイムパスワードや証明書認証な様々な認証方法を提供
- Azure Active DirectoryやAuth0が有名
IDaaSが役に立つユースケースをこれから説明します。例えば自社開発のオンラインストレージサービスを開始したいとします。でも、認証・認可の機能を作り込むのは超大変です。そこをIDaaSに任せてしまえば、餅は餅屋ということで、認証・認可はIDaaSに任せて、自社のサービス開発に専念できます。
色んなフロー
本章ではアクセストークンを取得するための色々なフローについて説明します。今回説明するのは、冒頭で説明したOAuthの4つの特徴のうち、3になります。
- 従来のID・パスワードベースとは異なるトークンベースの認証
- トークンには限られた権限だけを与えられているので、万が一トークンが盗まれても被害が少ない
- 今回はこちら → 認可コードフローなどによりトークンを安全に取得
- トークンには有効期限が設定されており、万が一トークンを盗まれても有効期限過ぎると使えないので、セキュア
アクセストークンを取得するためには、その前にOAuthの仕様に定められたフローが必要となり、主要なものとしては以下になります。
認可コードフロー |
主にWebアプリケーション向けのフローで、認可コードフローをやり取りして、アクセストークンを取得します。 |
インプリシットフロー |
スマホアプリやブラウザ上のJava Scriptで動くネイティブアプリから直接アクセストークンを発行する必要があるときのフローです。 ※ ただし現在は非推奨!! |
クライアントクレデンシャルズフロー |
クライアントIDとクライアントシークレットを投げるとアクセストークンが返ってきます。主にバッチ処理向けフローです。 |
リソースオーナーパスワードクレデンシャルズフロー |
認可サーバーに登録されているユーザー名とパスワードを投げるとアクセストークンが返ってきます。 |
一つずつ説明していきたいと思います
認可コードフロー
「OAuthをざっくり説明」のところで説明したフローが、認可コードフローになります。
インプリシットフロー
インプリシットの流れを説明します。ここではAさん(リソースオーナー)内のスマホアプリ(クライアント)が、Facebook(リソースサーバー&認可サーバー)の投稿一覧を取得するようなユースケースを考えてみます。まずアプリの開発者は、Facebookに対して、Oauthで接続させてくださいみたいな事前の申請をします。これは、Facebookなどのサービスに申請専用の画面が用意されています。その申請フォームに必要な項目を入力します。そのときに必ず、Facebookに対して行いたい作業(閲覧や投稿)を申請します。
申請が終わると、Facebookは「クライアントID」「クライアントシークレット」をアプリの開発者に発行します。またアプリの開発者から申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。Facebookは、そのクライアントID、クライアントシークレット、利用サービスを自身のデータベースに保存します。ここまででアプリの開発者の仕事は終わりです。この作業はOAuth利用時にアプリの開発者が最初の一度だけ行えばよい作業です。
ここでちょっとこの手順について追加の説明をします。インプリシットフローでは、クライアントID・クライアントシークレットは、クライアント(この場合だとスマホアプリ)に保存しません。なぜならば、インプリシットフローの場合は、クライアントであるスマホに、リソースオーナーが簡単にアクセスできます。つまりリソースオーナーがクライアントシークレットを取得できます。もしクライアントID・クライアントシークレットをスマホに保存すると、クライアントID・クライアントシークレットはかなり強い権限を持っているので(詳細は後述)、複数のリソースオーナー(アプリ使う人)が強い権限を持ってしまうためです。
ちなみに認可コードフローの図を以下に再掲しますが、この場合は、リソースオーナー(Aさん)はクライアント(Twitter)の中にあるクライアントIDとクライアントシークレットにアクセスできません。これはTwitterのサーバーの中に保存されているためです。なので認可コードフローでは、クライアントIDとクライアントシークレットをクライアントの中に保存します。
ということで、インプリシットフローに話を戻します。スマホアプリ内の埋め込みブラウザが、Facebookのログイン画面にアクセスします。
ここでちょっとこの手順について追加の説明をします。Facebook(認可サーバー)の認証画面にアクセスするためのURLについては先のページの通り以下になります。ここの書式はOAuthの仕様である程度決まっています。一般的にこのURLは「認可エンドポイント」と呼ばれており、その詳細は以下の通りとなります。
- 認可エンドポイントのURLです。これはリソースサーバー(FacebookやAzure Active Directoryなど)ごとに決まっています。
- response_typeというクエリパラメーターは、これから実施されるフローを決定づけるものです。tokenはインプリシットフローを表します。これがcodeだと認可コードフローになります。
- クライアントIDを指定します。この値が認可サーバーに渡されて、対応したクライアントIDに紐づく処理を行います。
何回も脱線してすみません。Facebook(認可サーバー)での認可が完了すると、事前にFacebook(認可サーバー)で設定したリダイレクトURIにリダイレクトされます。ここまでは認可コードフローと同じなのですが、違いはリダイレクトURIのハッシュフラグメント(#以降)にaccess_tokenというパラメータが付いていることであり、これがアクセストークンを表しています。
スマホアプリ(クライアント)は、先程のリダイレクトURIを解析して、ハッシュフラグメントからアクセストークンを取得して、スマホアプリ内に保存します。
スマホアプリ(クライアント)は、Facebook(リソースサーバー)にアクセストークンと送るとともに、投稿の一覧を要求します。
スマホアプリ(クライアント)は、Facebook(リソースサーバー)にから投稿の一覧を受け取ります。
ということでインプリシットフローの説明は以上になります。ポイントは以下の通りとなります。
- 認可コードフロートは異なり、認可サーバーへの認証完了のレスポンスで、ダイレクトにアクセストークンを受け取る
- アクセストークンがクライアント(スマホアプリ)⇔認可サーバー(Facebook)間を流れる、つまりパブリックなネットワーク上に流れるので、漏洩のリスクが認可コードフローより高い。
- インプリシットフローにはいくつかの脆弱性があり(後述)、今は非推奨。スマホアプリのようなネイティブアプリでも認可コードフローを用いる。
クライアントクレデンシャルズフロー
クライアントクレデンシャルズフローの流れを説明します。ここではバッチの開発者(リソースオーナー)が開発したバッチプログラムをバッチ処理サーバー(クライアント)に置いて、そのバッチプログラムからFacebook(認可サーバー&リソースサーバー)の投稿一覧を取得するようなユースケースを考えてみます。まずバッチ開発者は、Facebookに対して、Oauthで接続させてくださいみたいな事前の申請をします。これは、Facebookなどのサービスに申請専用の画面が用意されています。その申請フォームに必要な項目を入力します。そのときに必ず、Facebookに対して行いたい作業(閲覧や投稿)を申請します。
申請が終わると、Facebookは「クライアントID」「クライアントシークレット」をバッチ開発者に発行します。またバッチ開発者から申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。Facebookは、そのクライアントID、クライアントシークレット、利用サービスを自身のデータベースに保存します。そしてバッチの開発者は受領したクライアントIDとクライントシークレットを、バッチ処理サーバーに保存します。この作業はOAuth利用時にバッチの開発者が最初の一度だけ行えばよい作業です。
バッチ処理サーバー内のバッチプログラムは、クライアントIDとクライアントシークレットを渡して、アクセストークンを要求します。
Facebook(認可サーバー)は、クライアントIDとクライアントシークレットが正しいものであることが確認できたら、バッチ処理サーバー(クライアント)にアクセストークンを返します。
バッチ処理サーバー(クライアント)は、Facebook(リソースサーバー)に対して、アクセストークンを送付してリソース(この場合はFacebook投稿の一覧)を要求します。
Facebook(リソースサーバー)は、送付されたアクセストークンが正しいことを確認した上で、バッチ処理サーバー(クライアント)にFacebookの投稿一覧(リソース)を返します。
ここからはちょっと補足的なことを説明します。以下の図を覚えていますか?クライアントクレデンシャルズフローで、クライアントシークレットは不特定多数のひとにばらまいてはいけないと説明しました。それはクライアントシークレットが強力な権限を持つからなのでが、それは先のクライアントクレデンシャルズフローで説明したように、クライアントクレデンシャルズフローではクライントIDとクライアントシークレットでアクセストークンを取得できます。なので権限が強力なのです。
リソースオーナーパスワードクレデンシャルズフロー
リソースオーナーパスワードクレデンシャルズフローというものを説明します。基本は、クライアントクレデンシャルズフローと変わりませんが、クライアントID・クライアントシークレットの代わりにユーザー名とパスワードを渡します。
ここではバッチの開発者(リソースオーナー)が開発したバッチプログラムをバッチ処理サーバー(クライアント)に置いて、そのバッチプログラムからFacebook(認可サーバー&リソースサーバー)の投稿一覧を取得するようなユースケースを考えてみます。まずバッチ開発者は、Facebookに対して、Oauthで接続させてくださいみたいな事前の申請をします。これは、Facebookなどのサービスに申請専用の画面が用意されています。その申請フォームに必要な項目を入力します。そのときに必ず、Facebookに対して行いたい作業(閲覧や投稿)を申請します。
申請が終わると、Facebookは「クライアントID」「クライアントシークレット」をバッチ開発者に発行します。またバッチ開発者から申請があった作業(投稿や閲覧、以下利用サービスと呼びます)を独自の文字列に変換します。Facebookは、そのクライアントID、クライアントシークレット、利用サービスを自身のデータベースに保存します。そしてバッチの開発者は、Facebookにログインするためのユーザー名とパスワードをバッチ処理サーバーに登録します。
バッチ処理サーバー内のバッチプログラム(クライアント)は、Facebook(リソースサーバー)にリソースオーナーのユーザー名とパスワードを渡します。
Facebook(認可サーバー)は、ユーザー名とパスワードが正しいものであることが確認できたら、バッチ処理サーバー(クライアント)にアクセストークンを返します。
バッチ処理サーバー(クライアント)は、Facebook(リソースサーバー)に対して、アクセストークンを送付してリソース(この場合はFacebook投稿の一覧)を要求します。
Facebook(リソースサーバー)は、送付されたアクセストークンが正しいことを確認した上で、バッチ処理サーバー(クライアント)にFacebookの投稿一覧(リソース)を返します。
以上が、リソースオーナーパスワードクレデンシャルズフローの説明でした。このフローのメリットは、仮にOAuth適用前に基本認証(ユーザーIDとパスワードによる認証)を使っていて、OAuthに移行したとしても、バッチ処理サーバー(クライアント)がFacebookに渡す情報は変わらないということです。ということは、OAuthに変更したとしても、バッチ処理サーバーのSDKのバージョンだけ上げれば、そのままFacebookに渡す情報を変えることなくOAuth対応できます。
マイクロソフトもExchange Onlineの認証を基本認証からトークンベースのOAuth認証(マイクロソフト的には先進認証というらしいです)への移行を進めています。ただし、旧モジュールとの互換性を保つために、以下のURLにあるようにPowerShellのバージョンだけ入れ替えると、従来どおり基本認証(ユーザー名とパスワードを渡す方式)の形式を維持したまま、裏ではトークンベースの認証が行われているということです。まさしくこのモジュールはリソースオーナーパスワードクレデンシャルズフローを使っていると推測されます。
Exchange Online PowerShell V2 モジュールのバージョン情報
https://docs.microsoft.com/ja-jp/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps
ただし、マイクロソフト的には、この方法は非推奨のようです。この方法だと、せっかくOAuthにしたのに、アプリケーション内にユーザー名とパスワードが埋め込まれることとなり漏洩のリスクが大きく、OAuthにした意味がないということなのかもしれません。
まとめ
いかがでしょうか?OAuthというものがイメージできたら幸いです。至らぬ說明多々あったかもしれませんが、生暖かい目で見守って頂けたらと思います。
次回は、アクセストークンとリフレッシュトークンについて説明いたします。
とてもわかりやすい説明でOAuthについて楽しく学ぶことができました!