※2020年時点の内容です!
はじめに
本記事は、
- OAuthって言葉を聞いたことがない!
- 聞いたことはあるけど詳しくは知らない!
みたいな感じの方向けの記事です。僕もまだよくわかっていません。OAuthを実装したことがある方にはちょっと簡単すぎるかもしれません。また、そういう方がいらっしゃったらぜひ温かい目で流して、おかしなところがあればコメントお願いします!
OAuthとは?
ざっくり
OAuth (オー オース?)とは、一番簡単に説明するならば、 これ
とか これ
です。これがOAuthです。みなさんも一度は目にしたことがあると思います。一見ただの認証画面のように見えるけれど、実はこれは立派なセキュリティシステムなんです。今回はこれをざっくり説明していきます。
もう少し細かく
OAuthを理解するうえで重要になってくる 2つの概念 があります。それは、
- 認証(Authentication)
- 認可(Authorization)
です。それぞれ説明していくと、
-
認証(Authentication) 認証は、通信の相手が誰であるかを確認すること。純粋な認証 は、それが完了しても、何かの許可が下りるということはない
-
認可(Authorization) 認可は、とある条件に対してリソースへのアクセスの権限を与えるこ とで、純粋な認可には、「誰」という概念が存在しない
です。図はすみません。これを踏まえてOAuthの正体を簡潔に言うと、「認可情報の委譲」の仕組み、オープンスタンダード です。認可情報は、認可によって得られた権限等を指します。
①あるサービスのリソースへのアクセスの権限を、②リソースの持ち主でない他のサービスに与える、これを安全に執り行うための仕組みです。
ゲームアプリのアカウント登録をGoogleのアカウント情報を用いて行ったり、気になるニュースを見つけたときに、ニュースサイトから出ずにTwitterで共有するときなどに上の画像のような画面が出てきますが、ここでの①あるサービス にあたるのがGoogleやTwitterで、アカウント情報や投稿機能にあたるのがリソース、②他のサービスにあたるのがゲームアプリやニュースサイト です。
OAuthのフロー
OAuthのバージョンについて
OAuthには、OAuth1.0とOAuth2.0という2つのバージョンがありますが、最初はOAuth2.0のほうを見ていきます。
また、OAuth2.0には 4つの種類(grant) があり、今回はその中でも Client Credentials Flow というものを例示します。(イメージしやすそうなので)
準備
OAuth2.0(Client Cledentials flow) の登場人物は以下の通りになります。
名前 | 例 |
---|---|
ユーザー | クライアントアプリを利用しているユーザー |
クライアント(アプリ) | ゲーム、ニュースサイト等の、認可を受け取りたいサービス |
サービスプロバイダー | Twitter,Google等、リソースを提供する側のサービス |
認可サーバ | Twittre、Google等の、認可情報を取り扱うサーバ |
リソースサーバ | Twitter,Google等の、リソースが保管してあるサーバ |
クライアントは事前にサービスプロバイダーにアプリケーションを登録しておきます。 (twitterでの例)
ここで、consumer_keyとconsumer_secretという値、redirect_urlを受け取ります。これで準備完了です。
OAuth2.0のフロー(Client Cledentials Flow)
③では、consumer_keyとconsumer_secretを埋め込んだリクエストを送ります(HTTPS,POST)。 ⑤では、元々ユーザーがサービスプロバイダーに登録してあるidとpasswordを入力します。
実装例
クライアントはローカルなアプリ(それこそ、ゲームアプリやニュースアプリのようなもの)で、URLはブラウザで開かれるとします。
⓪:クライアントは事前にサービスプロバイダーにアプリケーションを登録しておき、consumer_keyとconsumer_secretという値を受け取っておく。 ①:ユーザーは認証開始ボタン等を利用し認証を開始する。 ②クライアントは、ブラウザを認証ページにリダイレクトさせる。 ③:クライアントは、認可サーバにredirect_urlとconsumer_keyとconsumer_secretを埋め込だリクエストを送る。 ④:クライアントは、ブラウザに認証画面を表示する。 ⑤:ユーザーは、認証画面にidとpasswordを入力する。 ⑥:認可サーバはブラウザをコールバックURLにリダイレクトさせる。このときに、認可サーバは認可コードを発行する。 ⑦:ブラウザは、認可サーバにredirect_urlと⑥で受け取った認可コードを埋め込んだHTTPSリクエストを送る。 ⑧:認可コードを受け取った認可サーバは、レスポンスでクライアントにアクセストークンという文字列を渡す。 ⑨:クライアントは、アクセストークンを用いてAPIを呼び出す。
①から⑤までが「認証」 で、⑥から⑨までが「認可」 のフローです。ユーザーがサービスプロバイダーにidとpasswordを渡すのは認証、サービスプロバイダがクライアントに渡すのが、認可情報です。
APIって何?
ここまで説明なしで当然のように使ってきたAPI という単語の説明をします。
サービスプロバイダーが提供している様々な機能などのリソースを、機能ごとに外部が使いやすいように作られたプログラムをまとめたもの、アプリケーションからプラットフォームの機能を呼び出すための仕組みを**API(Application Programming Interface)**といいます。例えばTwitterでは、ツイートをするAPI、タイムラインを取得するAPI、フォロワーを取得するAPI、DMをするAPI等があります。
つまり、この例でいうOAuthにおける「認可情報の委譲」とは、クライアントがサービスプロバイダーの提供するAPIを呼び出すこと、呼び出す「権利」をアクセストークンという形で「委譲」されること、です。
ちなみに、Twitterのような場合では、生成されるアクセストークンはaccess_tokenとaccess_token_secretのように分かれていて、サービスプロバイダー(Twitter等)の各アカウントごとにのものが紐づけられるので、OAuthは、認証をしたアカウントに紐付いたアクセストークンをクライアントに渡すという形をとっています。
OAuth1.0について
OAuthの場合、2.0は1.0の純粋なバージョンアップだというわけではないという意見もあります。その例を、OAuth1.0で先の例と同じものを実装することで可視化してみます。
準備
OAuth1.0でも、OAuth2.0と同様にクライアントは事前にサービスプロバイダーにアプリケーションを登録しておき、consumer_keyとconsumer_secretという値、redirect_urlを受け取っておきます。
OAuth1.0でのフロー
②では、consumer_keyとconsumer_secretを署名(Signature)の形にしてから渡し(HTTP,POST)、リクエストトークンという文字列を要求します。 ⑨では、③でもらったリクエストトークンを渡し、アクセストークンを要求します。
実装例
2.0の時と同様に、クライアントはローカルなアプリで、URLはブラウザで開かれるとします。
①ユーザーは、認証開始ボタン等で認証を開始する。 ② クライアントは、認可サーバにredirect_urlとconsumer_keyとconsumer_secretを署名(Signature)にしてから渡し、リクエストトークンを要求する。 ③認可サーバは、クライアントにレスポンスでリクエストトークンを発行する。 ④クライアントは、ブラウザを認証ページにリダイレクトさせる。 ⑤ブラウザは、認可サーバにリクエストトークンを渡す。 ⑥認可サーバは、ブラウザに認証画面を表示する。 ⑦ユーザーは、ブラウザの認証画面を介して認可サーバにidとPasswordを渡す。 ⑧認可サーバはブラウザをコールバックURLにリダイレクトさせる。 ⑨ブラウザは、認可サーバにredirect_urlとリクエストトークンを渡す。 ⑩認可サーバは、クライアントにアクセストークンを発行する。 ⑪クライアントはアクセストークンを用いてリソースサーバからAPIを呼び出す。
OAuth1.0の署名(Signature)とは
https://syncer.jp/Web/API/OAuth/ がとてもわかりやすくまとめられています。(現在は、TwitterもOAuth2.0も採用していたような気がします。) 要約すると...
-
署名とは、URLとあるトークンを組み合わせて暗号化すること
-
OAuth1.0では、HMAC-SHA1という方式で署名を組む
具体的な方法は、
①consumer_keyとconsumer_secretをURLエンコードし、&でつなげる、これをキーと呼ぶ ②トークン(リクエストメソッド(POST)、URL、パラメータ)をURLエンコードし、&でつなげる、これをデータと呼ぶ ③データ、キーを使って、HMAC-SHA1方式のハッシュ値を生成する ④③で得られた値をbase64エンコードする
工程にハッシュが含まれているので、署名は不可逆な暗号になり、consumer_keyとconsumer_secretを奪おうとする攻撃に対する安全性が非常に高いと言えます。
1.0と2.0の違い
2.0では、consumer_keyとconsumer_secretをそのままの形で認可サーバに渡しているのに対し、1.0では、署名という形に変形してから認可サーバに渡しています。 そのかわり、2.0ではリクエストの部分をHTTPSを使うよう強制し、認可コードというパラメータを用いてアクセストークン発行時の本人確認をおこなっています。
...なんだか1.0のほうが安全そうに見えますね。1.0は、「consumer_keyとconsumer_secretを盗られたら絶対にダメ、これらを絶対に盗られないようにしよう」という指向なのに対し、2.0は、「たとえconsumer_keyとconsumer_secretが盗られたとしても、安全性を保てるようにしよう」という指向で設計されたようです。
実際、1.0と2.0はどちらがセキュアか、という議論は賛否両論です。
では一体何が改善されているのでしょう。
それはズバリ、「実装が容易になった」ことです。
1.0は、特に署名を組む部分の実装が重くて、なかなか手軽に導入できなかったそうですが、2.0は構造を単純にしたことによって、実装が容易になり、今では様々な言語でOAuthを実装するためのライブラリがととのえられています。
結局、何が嬉しいのか
ここまでごちゃごちゃ説明したけれど、いまいち利点が見いだせなかったという方もいると思います。 先と同じことを、一般的な(Basic認証と呼ばれる)認証を用いて実装する例と比べると、わかりやすく利点が浮き出てきます。
どうでしょう、違いは見えましたか? OAuthの最大の特徴、OAuthが安全と謳われる理由は、 「サービスプロバイダーのidとpasswordを、クライアントアプリ直接渡さずして、クライアントアプリに任意の権限を与えることができる」 です。
認証、認可の構造
認証と認可の話を思い出して簡単に図示してみます。
通常の「認証」と呼ばれるフローでは、純粋な認証をidとpasswordを用いて行い、純粋な認証で得た情報をもとに、認証結果が登録ユーザなら権利を渡す、のように純粋な認証と純粋な認可を組み合わせています。(一般にはこれを「認証」と呼ぶ気がします)また、クライアントがユーザーと同等の立場で純粋な認証しているのがわかると思います。認可サーバからしたら、ユーザーとクライアントの見分けが付かない状態です。そういう状態なので、認可サーバはユーザーが持つのと同等の権利をクライアントに与えます。
一方、OAuthを見てみると、
組み合わせ方自体は通常の場合と同じですが、権利が、認証をしたユーザー本人ではなく、クライアントに渡されているのがよくわかると思います。わかりにくかったら図のせいです。すみません。
クライアントに権利をしたい場合は、本来左のような形で認可を行いたいですが(?)、idとpasswordをクライアントに渡さないために右のように変形します。Basic認証では、認可サーバからみてユーザー=クライアントだったので、権利もクライアントのものとみなされましたが、OAuthでは、本来はユーザーの持つ権利を、関係のない(?)クライアントに委譲しています。
モヤっとしたことメモ、独り言
いくつか調べていて、OAuthを「認可の委譲の仕組み」だと主張している記事、文献がいくつかあったけれど、純粋な認可には「誰」という情報が関係なくて、認証情報の出処と認可情報を渡す先が一致する必要はないから、図の左から右のような変形があったとしても、認可を委譲していることにはならないと思う。ということは、認可情報を委譲している、という本記事冒頭での主張も崩れてしまうように思える。
→認可情報、つまりリソースへのアクセス権限は、本来ユーザー(ユーザーの、サービスプロバイダーのアカウント)が持つものだから、それを委譲するという意味での「認可情報の委譲」、認可処理とつながっている矢印を気にするというよりは、アクセス権の本来の所有者を気にした意味での「委譲」だということ?
要するに
Basic認証のようにidとpasswordをクライアントアプリに渡してしまうと、クライアントアプリはユーザーと同等に権利を得てしまい、クライアントが悪質な場合、サービスプロバイダーのアカウントが簡単に乗っ取られてしまったりします。
一方、OAuthを用いると、idとpassword(図では認証情報とした)がクライアントアプリの手に渡らないので安心です。
また、これ
を見ると、何やらクライアントが呼び出す機能が事前に告知されているのがわかります。 OAuthでは、クライアントはあらかじめサービスプロバイダーに呼び出す機能の情報を登録しておく必要があり、登録した情報は、認証の際に必ずユーザーに見える形で表示されるので、ユーザーにとって、ある程度危険を回避することが可能になります。
つまり、こういう形です。
(?)
なんとなくまとめ
これだけ目を通せば、なんとなく知ったふりをできると思います。
- OAuthは、「認可情報の委譲」の仕組み
- OAuthは、サービスプロバイダーのidとpasswordをクライアントアプリに渡さないことで安全性を確保している
- OAuthは、クライアントが必要以上の権限を持たないように事前に設定する、設定を認証の際にユーザに見せる
- OAuth1.0は、consumer_keyとconsumer_secretを署名化することで、これらを盗られるのを防いでいるが、実装が重い
- OAuth2.0は、consumer_keyとconsumer_secretの保護をHTTPSに任せ、認可コードを導入することでこれらを盗られた時の対策をしている、1.0と比べて実装が軽い
(実装面はライブラリの普及でクリア?)
終わりに
いかがでしたか?OAuthというものの概形がなんとなくつかめたでしょうか? また、これらを実装するときには、各言語で様々なライブラリが普及しているので、思ったより簡単に実装できると思います。ただライブラリを使うのではなく、内部でこういう処理をしているんだなぁということがわかっていれば、より簡単に実装ができると思います! Pythonでは、 https://qiita.com/mikan3rd/items/686e4978f9e1111628e9 等が参考になると思います。
書いているうちにごちゃごちゃしてしまったり、Twitter関係なくなってしまったりでとても読みにくかったと思います。特に図は最後に読みかえしてわかりにくかったから急いで付け足したようなものなので、見た目があれです。最後まで目を通してくださってありがとうございました!
参考
https://dev.classmethod.jp/security/authentication-and-authorization/
https://openid-foundation-japan.github.io/rfc6749.ja.html
https://ja.wikipedia.org/wiki/OAuth
https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f