OAuth2やOpenID Connectとは何か、なんとなくわかった気になるための概要

今更ですが備忘のため,OAuth とか OpenID とかその辺の概要についてまとめるメモ.具体的な仕様については書かないけど RFC や公式サイトに載っている.便宜上,厳密性を疎かにしている点あり.

OAuthについて

背景

現在,Twitter や Yahoo など様々なサービスが,Web API として機能を提供しています.API エコノミーなんていう言葉がありますが,Web API としてサービスを提供していくこの動きは今後も続きそうです.

これに伴って,API をラップしてサービスを提供するサードパーティ製のアプリケーションが続々と出てきています.それをここでは「クライアント」と呼びますが,サービスを使いたいユーザはこのクライアントを通して API を利用し,何かしらの価値を享受するわけです.この方式が現在の Web 世界ではよく普及しています.

例えばユーザが Twitter クライアントを使うと,クライアントは TwitterAPI を叩き,タイムラインを取得して結果をユーザに表示したりするわけですね.基本的にはこのように「ユーザ ↔ クライアント ↔ API」という関係になります.クライアントは API サービスのフロントエンドとも言えるかもしれません.

この際,クライアントからリクエストを受けたAPI 提供側は,どのユーザからのリクエストなのかを判別する必要があります.API 提供側が,自分を使おうとしているクライアントの向こうにいるユーザが誰なのかを検証できなければ,ユーザが他人になりすまして他人の情報を知ったり,他人のアカウントで活動することが可能になってしまうからです.

Twitter で言えば,他人になりすましてダイレクトメッセージを取得する API を叩ければ,盗聴できてしまいます.これは,他人のアカウントでWeb サービスにログイン出来てしまうようなものです.

つまり,普通(?)の Web サービスを利用する時と同じように,API の利用/提供においても,ログインのような認証/認可の仕組みが必要になるわけです.が,この認証/認可について,一般的な Web サービスと同様に考えることはできません.これは,「クライアント(第三者)の存在の有無」という違いがあるためです.

このため,API の世界では,クライアントから API 提供側に対して「ユーザからは許可をもらっているから,ユーザ情報を扱う API を使わせてくれお」というような要求が発生し,そのセキュリティチェックが発生することになります.要は,第三者が存在するため,それを含めた間接的なセキュリティチェックが必要になるのです.では,API 提供側は,あるクライアントがあるユーザの許可をもらったことをどうやって検証すれば良いのでしょうか.

ここで,ユーザがクライアントに ID/PW(API提供側サービスのモノ) を渡しておく方法が容易に思いつきます.クライアントはその情報を使って,ユーザに成り代わるような形で API を叩くということです.API 提供側は送られてきた ID/PW を見て,クライアントの向こう側にいるユーザを特定し,本人にしか渡さない情報を返したりします.

Twitter の例で言うと,ユーザが Twitter の ID/PW を Twitter クライアントにあらかじめ渡しておけば,クライアントはその情報を使って Twitter にアクセス(ログイン)し,当該ユーザ情報の取得や操作ができるようになるよね,ということです.

しかし,この方式にはいくつか問題があります.例えば,ID/PW をクライアントに渡してしまうと,基本的にはクライアントがそのアカウントの全ての権限を持つことになります.クライアントに「タイムラインの閲覧だけを許可する」といった制限をかけるのが難しくなるということです.悪意のあるクライアントを想定した場合にはより深刻な問題になりますし,クライアントから情報が漏れたときのインパクトが大きなものになります.すなわち,できればクライアントという謂わば第三者にユーザの認証機密情報を渡すことは避けたいわけです.

また,ユーザがあるクライアントに 自分の ID/PW を使って API を叩くことを取りやめさせる場合には,ID/PW を変更せざるを得なくなります.この時,同じ ID/PW 情報を使っている他のクライアントにもこの変更の影響が出るため,アクセス許可の取り消しのような変更を個々のクライアントに適用することが難しくなります.

で,このように第三者が存在する API の世界で,イイ感じにセキュリティチェックをやろうというのが OAuth です.

概要

上に書いたように,ID/PW を単純にクライアントに譲渡する方法では,様々な問題が起こり得ます.そこで OAuth では,ID/PW をクライアントに教えずに済む方法が採用されています.すなわち,ID/PW 以外の何かで API アクセスの認可をするということです.

ちなみに「認可」というのはアクセス等の許可をすることで,「認証」とは別です.「認証」は簡単に言えば本人を確認することで,例えば,ID/PW での認証や指紋認証があります.この辺は割愛.考え方は次の記事になんとなく書いてあります.認証と認可の違い - ITmedia エンタープライズ

で,OAuth ではトークンというものを使って API アクセスの認可をします.トークンは,API を使うための鍵のようなものです(実際はタダの文字列ですが).ユーザがクライアントに自分の情報へのアクセスを許可した証として発行されるイメージです,

具体的には,API を使う側(クライアント)はこのトークンを API リクエストと一緒に送ることで,その API の使用を認可されます.API 提供側は送られてきたトークンを検証することで API へのアクセスを許可します.すなわち,クライアントは「ユーザに許可を得た証としてトークンを持っているから,ユーザ情報を扱う API を使わせてくれお」という感じで API 提供側にリクエストを送ることになります.

OAuth ではこんな感じでトークンを使って認可をします.となると,じゃあクライアントは一体どうやってトークンを手に入れるんだ,という話になります.トークンは,例えばある特定のアカウントへのアクセス許可証なわけですから,誰にでも渡していいわけではありません.ユーザに紐付いたトークンを渡していいのは,そのアカウントを持ったユーザ本人だけです.ここで「認証」が必要になります.

ユーザがクライアントを使おうとすると,API 提供側からユーザに対して,認証とトークンの発行許可が求められます.クライアント視点だと,「ユーザさん,私はトークンが必要です.API提供側に本人証明をして,私にトークンを発行するよう連絡してくだし」という感じです.

API 提供側がユーザからその連絡を受けて認証が完了すると,クライアントにトークンが発行されます(実際にはもう少し複雑な流れがあったりしますが).この「ユーザ ↔ API 提供側」間の認証は基本的にユーザの ID/PW で行われます.もちろん,ここで認証をするのは他人になりすましてトークンを発行されることを防ぐためです.この点から,トークンを持っている=ユーザ本人から認証およびトークンの使用許可を得た,と判断するような形で,API の使用を認可するのです.

ここで,OAuth ではトークンで認可が行われるため,クライアントに ID/PW を教える必要がないということが重要です.クライアントはアクセス許可証としてのトークンを持つだけです.これで上に挙げたいくつかの問題を解決できます.

例えば,このアクセス許可証にはアクセス有効範囲が設定されます.つまり,ID/PW を渡してしまう場合の課題の1つだった,無駄に権限を与えてしまう問題をシンプルに解決できます.Twitter の例で言えば,ある Twitter クライアントがもつトークンでは,タイムラインの取得はできるけど,ツイートはできない,という状態を作れたりします.

ここではざっくりと概要を書きましたが,トークンの具体的な発行方式にも色々あって,ケースに応じて使い分けられています.以下,それについて説明されている記事です.

OAuth 2.0 全フローの図解と動画

まとめると

クライアント(第三者)が存在する Web API の世界では,それを含めた間接的なセキュリティチェックの仕組みが必要になります.ユーザが ID/PW をクライアントにわたす形でこれを実現しようとすると,色々な問題が発生してしまいます.そこで,「認証」と「認可」を切り離し,トークンを使ってクライアントを「認可」する仕組みを導入します.ユーザと API 提供側との間には「認証」が発生し,その結果としてトークンがクライアントに渡されます.クライアントと API 提供側との間にはトークンによる「認可」が発生します.この際,クライアントはユーザの認証情報に触れずに,認可を得ていることがポイントです.

OpenID Connect について

概要

Web サービスなどを使っていると,基本的にはサービスごとに ID/PW の組み合わせ=認証情報を用意することになります.が,これは結構問題です.

沢山のサイトを使うとすると,ユーザ側での認証情報の管理が非常に煩雑になり,不便です.また,そもそも ID/PW での認証は,PW がバレてしまえばいくらでもなりすましできてしまう点で,脆弱でもあります.

このようなことから,ID/PW によらない方式で,1つの認証情報で複数のサービスを使えるようにしたいというモチベーションが生まれます.OpenID Connect は,このような課題にアプローチするために発展してきた技術と言えます.SSO(シングルサインオン)的なイメージでしょうか.これができれば,ユーザはサービスごとに個別の認証情報を用意して管理する手間から開放されます.また,サービス提供側としては,ユーザの認証情報を管理するリスクをヘッジできます.

「あるサービスで認証された結果」を複数のサービス間で使い回すことができれば,それを実現できます.例えば,Yahoo! で認証されたことを示す証明書のようなものが存在し,他のサービスがその証明書を使ってログイン(認証)することを許してくれればいいのです.「Yahoo!で認証されていることを確認できたからそのIDでウチにもログインしていいよ」というような世界です.

実は OpenID Connect でも,OAuth と同様にトークンを使ってこれを行います.上に書いた証明書の役割を果たすのがトークンです.OpenID に対応しているサービスは,トークンを得ることで他のサービスの ID と連携できます.ユーザから見れば,あるサービスの ID を使って別のサービスにログインできるということです.トークンを発行するのは,プロバイダと呼ばれる機関です.Yahoo!はてななどがこれに当たります.

ユーザがとあるプロバイダの OpenID(OpenID Connect でログインするためのID) で第三者サービスにログインしようとすると,そのプロバイダからユーザに対して,認証とトークンの発行許可が求められます.第三者サービスの視点だと,「ユーザさん,あたなを認証するためにトークンが必要です.プロバイダに本人証明をして,私にトークンを発行するよう連絡してくだし」という感じです.

トークンを得られた第三者サービスは,ログインしようとしているユーザが本当にプロバイダから認証されたのかを確認します.OK なら,プロバイダが提供している ID と自社サービス内の ID を紐づけ,ID 連携が完了します.

ちなみに,ユーザが第三者サービスに新規登録する際などはプロフィール情報を入力する必要があります.OpenID Connect では,このような入力を支援するための仕様も定義されています.第三者サービスはプロバイダ側の ID を参照して基本的なユーザ情報を取得し,予めプロフィール情報などをセットできるようになります.折角 ID を連携するのだから,ユーザ情報の初期化等に連携元の情報を少し使わせてくれ,という感じです.

さて,このトークンの発行から利用の大きな流れについて,OAuth と全く同じであることに気が付かれたかもしれません.というのも,OpenID Connect は OAuth の仕様をベースにしているためです.では,これらの違いは何なのかについて,なんとなくの位置づけを次に書きます.

OAuth との比較

OAuth と OpenID Connect の大きな違いとして,「OAuthは認可のために使うが,OpenID は認証のために使う」ということがよく言われています.OAuth におけるトークンは,API へのアクセス権です.すなわち,トークンは API 利用の認可に使われます.一方で,OpenID はサービスにアクセスしたきた人を認証するためにトークンを使います.

ここでまず,OAuth のトークンを OpenID のような認証に使うことを考えてみます.両者が似たような仕様なのであれば,OAuth でも認証ができそうです.OAuth でも,トークンを持っている=ユーザ本人から認証およびトークンの発行許可を得た,と判断することができるため,トークンを持っていたら認証,とすればいいのではないかという話です.ちなみにここでいう OAuth での認証というのは,OAuth のトークンをログインのための認証情報として使うという意味です.

さて,OAuth の文脈でのトークンというのは,API の利用権です.これはしばしば鍵に例えられます.鍵を持っているからアクセスロックを解錠して API を使えるという感じです.OAuth で認証をするというのは,鍵を持っていればその鍵の所有者として認証する,というやり方です.これはセキュリティ的にまずいです.

なぜなら,OAuth では,鍵とその所有者のマッピング,すなわちトークンとユーザのマッピングについては考慮されていないのです.言い換えれば,OAuth でのクライアントは,その鍵を使おうとしている人が誰なのかは考えないわけです.なので,安易に OAuth で認証しようとすれば,鍵を持っていれば本人でなくても認証してしまいますし,自分の鍵だと言い張って他人の鍵を渡したのにも関わらず認証してしまうことすら考えられます.

しかし,これは OAuth の欠陥というわけではありません.OAuth は認可のための仕組みなので,このような認証については対象外で OK なわけです.そこで,OAuth とは別に,OAuth の仕組みをベースに認証を可能にした仕様が OpenID Connect なのです.

OpenID Connect ではトークンが大きく拡張されており,トークン自体に認証のための情報が付加されています.これにより,上に書いた OAuth での認証で発生していたような問題に対処しています.すなわち,OpenID Connect の文脈でのトークンというのは,非 ID/PW 形式の認証情報なのです.これは,OAuth でのトークンと役割が異なります.

OAuth でのトークンはタダの単一のキー文字列ですが,OpenID Connect のトークンは JWT と呼ばれる形式の構造化データです.トークンのデータ構造を拡張することで,認証の仕組みを実現しているわけですね.OAuth と OpenID Connect ではトークンの発行や利用の大きな流れは一緒でしたが,主にはこのようにトークンの利用目的や実態が異なっているのです.

まとめると

利便性向上を主な目的として,1つの ID を複数サービスで使い回せる仕組みとして発展してきたのが OpenID Connect です(SSO を実現する他の手段として SAML 等もありますがここでは割愛).OpenID Connect は OAuth をベースにした仕組みであるため,トークンの発行や利用の大きな流れは非常に似ています.しかし,「認証」か「認可」という枠組みで,実現したい目的や思想が異なります.OpenID Connect では,主にトークンのデータ構造を拡張することでトークンによる「認証」を実現しています.


仕様書とか参考になりそうなもの

一番分かりやすい OAuth の説明

一番分かりやすい OpenID Connect の説明

RFC 6749 - The OAuth 2.0 Authorization Framework

単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる | @_Nat Zone

Final: OpenID Connect Core 1.0 incorporating errata set 1