この章の目的
-
認証 と 認可 と Single Sign-On についていろいろ理解するための土台知識を得る
- この章だけややテキスト多め
- どんな単語が OAuth に関係している単語か、マッピングしておく
- OpenID Connect の理解の準備知識とする
概要
なぜ必要になったか
何らかの Twitter のサードパーティクライアントがあるとする ( 以後クライアント )
ユーザがクライアントを用いて Twitter の機能を使う際、クライアントが Twitter の ID/PW を必要とする方法には、以下の問題がある
- 複数の Twitter クライアントを用いる場合、全クライアントで ID/PW を入力する必要がある
- クライアントが Twitter にログインできるので全機能にアクセスできる
- 悪意のあるクライアントだったら大変
- クライアントから Twitter へのアクセスを遮断したい場合、Twitter の ID/PW を変えるしかない
- 他にも別の Twitter クライアントを使っている場合、そちらの再設定も必要
- クライアントが攻撃を受けて ID/PW が流出した場合、攻撃者が Twitter の全権アクセスを得る
これらの問題を解消するために、ID/PW と切り離した 認可 の仕組みが考えられた
( いくつかは Single Sign-On まで理解する必要がある )
関連する要素・流れ
- OAuth 1.0 リリース ( RFC5849 / 2010 )
- OAuth 2.0 リリース ( RFC6749 / 2012 )
- OAuth 1.0 をベースにしつつも、大幅見直し
- 互換性もない
- シンプルでセキュアになった
- PKCE リリース ( RFC7636 / 2015 )
- OAuth 2.0 の拡張仕様
これ以降は特に明記しない限り OAuth は OAuth 2.0 を指すものとする
概略
OAuth を調べるときはこれくらいを押さえていればスムーズだろう、というものを整理する
日本語のわかりやすい資料がたくさんあるので、詳細を知りたい場合はそちらを参照してほしい
4つのロール
OAuth のフローは4つの登場人物がいる
リソースオーナー
- リソースの所有者
- だいたいユーザのこと
クライアント
- リソースを利用するアプリケーション
- Twitter の例だと、サードパーティクライアントのこと
- サーバサイドアプリケーションなどを、コンフィデンシャルクライアントと言う
- ネイティブアプリケーションなどを、パブリッククライアントと言う
リソースサーバ
- リソースを管理しているアプリケーション
- Twitter の例だと、Twitter ( リソースが Tweet とか )
- ここへのアクセスには アクセストークン が必ず必要
認可サーバ
- アクセストークン を発行するサーバ
- Twitter の例だと、Twitter になるのかな
4つの値
細かい API パラメータはもっとあるけど、大局を把握するために4つとした
クライアントシークレット
- 認可サーバ が クライアント の身元を確認するために用いる
- 事前にリソースサーバのコンソールなのでクライアントを登録しておき、払い出してもらう
- POST で渡したり Basic 認証で渡したりする
- この方法ではなく鍵で身元を証明する方法もある
認可コード
- リソースオーナー が クライアント へ権限委譲することを同意した証
- 手に入れるためには 認可サーバ への認証と権限委譲の同意が必要
- リクエストでスコープを指定する ( アクセストークン を参照 )
アクセストークン
- クライアント から 認可コード を受け取った 認可サーバ が払い出す
- クライアント が リソースサーバ にアクセスするときに必ず用いる
- 2つの重要な属性を持つ
- スコープ
- 許される権限
- Twitter の例だと、ツイートはして良いが DM は認めない、など
- 有効範囲
- スコープ
リフレッシュトークン
- アクセストークン の有効期限が切れたときに、再度払い出すために用いる
- クライアント と 認可サーバ の間でやり取りされる
- 標準仕様では必須とはなっていない
3つのエンドポイント
この3つは標準仕様で定められている
認可エンドポイント
- 認可サーバ の URI
- リソースオーナー がブラウザから呼ぶ
- 認可コード の発行をする
トークンエンドポイント
- 認可サーバ の URI
- クライアント が呼ぶ
- アクセストークン の発行をする
リダイレクトエンドポイント
- クライアント の URI
- 認可コード を返却した 認可サーバ のリダイレクトによって、リソースオーナー がブラウザから ( 自動的に ) 呼ぶ
- クライアント に 認可コード を伝える
ロールと値とエンドポイントの整理
誰が登場し、どのエンドポイントが、誰と誰の間でどう使われるか
要点は次の通り
- クライアントに ID/PW のような認証情報を渡さなくて良い
- クライアントは 認可コード で アクセストークン を引き換える
- クライアントはユーザの指定したスコープで リソースサーバ にアクセスする
-
リソースサーバ は アクセストークン に基づき処理を行う
- スコープを解釈する方法は2つあるが、ここでは割愛
- アクセストークン 自体にスコープを意味する値を含める
- 都度 リソースサーバ にスコープを問い合わせる
認可グラントの4つのタイプ
アクセストークン を手に入れるまでのフローが4つある
標準仕様では以下の様に書かれており、個々のタイプ名にはグラントともフローとも付いていないが、日本語だとどっちかを付けて言うのが大半みたい?
1.3. Authorization Grant
An authorization grant is a credential representing the resource
owner's authorization (to access its protected resources) used by the
client to obtain an access token. This specification defines four
grant types -- authorization code, implicit, resource owner password
credentials, and client credentials -- as well as an extensibility
mechanism for defining additional types.
1.3.1. Authorization Code
1.3.2. Implicit
1.3.3. Resource Owner Password Credentials
1.3.4. Client Credentials
この本では以後フローと書く
API のパラメータ名まで標準仕様で定められているので、詳細は割愛する
これらを聞いたら「あぁ OAuth のフローのことだったな」くらいがすぐ思い浮かぶと理解と疎通がスムーズになる
認可コードフロー
- 最も重要で最も複雑
- コンフィデンシャルクライアントに最適化されたフロー
- セキュア
- アクセストークン がブラウザを経由しない
- 確認が多い
- この本ではこれだけ図で整理する
- これが整理できればあとのフローは足し引きで理解できると感じたため
詳細手順とパラメータは簡単に調べられるので割愛する
先ほども整理した通り、ID/PW がクライアントを経由しない
ただしパブリッククライアントの場合は、クライアント ( アプリ ) が認可リクエストを送る際にブラウザを起動して行うのが一般的であり、そのタイミングで ID/PW がブラウザを経由する
またブラウザが認可レスポンスの認可コードをクライアント ( アプリ ) に伝える際に、リダイレクト先クライアント ( アプリ ) がなりすましを行なっているリスクがある ( PKCE で後述 )
インプリシットフロー
- パブリッククライアントに最適化されたフロー
- OAuth 2.0 が登場した頃は、JS でから別のドメインにアクセスできなかったため
- 今は 非推奨
- CORS で別ドメインにアクセスできるようになった
- アクセストークン がブラウザを経由する点が脆弱
- 認可コードフロー + PKCE が推奨されている ( 後述 )
リソースオーナーパスワードクレデンシャルフロー
- リソースオーナーの ID/PW がクライアントを経由して認可サーバに行く
- 現在は 使ってはならない とされている
クライアントクレデンシャルフロー
- リソースオーナーが登場せず、アクセストークン はクライアント単位で払い出される
- ユーザが管理しないコンテンツをバッチ処理からアクセスする場合とかに用いる
PKCE
- Proof Key of Code Exchange の略でピクシーと読む
- OAuth 2.0 の拡張仕様として RFC7636 で定義されている
- 超ざっくり言うと、パブリッククライアントの認可コードフローでリダイレクトエンドポイント先のなりすましを防ぐもの
- リダイレクトにカスタムスキーム [1] を用いるが、同じスキームの悪意あるアプリをインストールしてしまっている場合、そっちが起動するリスクがある
- クライアントからブラウザを起動する段階でランダム文字列を渡しておき、その値を認可リクエストにそのまま含む
- 認可レスポンスのリダイレクトで起動したクライアントは、トークンリクエストに同じ値を添えなければ認可サーバにリジェクトされる
- クライアントがなりすましている場合、同じ値を用意できない
- 当然クライアントも認可サーバも PKCE に対応していなければいけない
フローの手数はほぼ変わらないので、code_verifier
, code_challenge
, code_challenge_method
というパラメータを見たら「あぁ PKCE 対応してるんだな」くらいがすぐ思い浮かべば、まずは十分
攻撃・脆弱性
厳密な手口名はわからないが、いずれも参考資料 2 から
詳細は割愛するが、OAuth のここまでの概要を把握できていれば、必要ならそれぞれ調べれば十分理解できるはず
攻撃・脆弱性
リダイレクトエンドポイントの Cross-site Request Forgery ( CSRF )
- リダイレクトエンドポイントを攻撃者が悪意ある URI に差し替える手口
-
state
パラメータを使うことで対応できる- 認可リクエストの
redirect_uri
パラメータと認可レスポンスのLocation
に添えるランダム文字列
- 認可リクエストの
オープンリダイレクト
- 認可リクエストの
redirect_uri
パラメータに攻撃者の URI を設定させる手口 - 認可サーバに事前にリダイレクトエンドポイントを登録しておくことで対応できる
Reffer ヘッダによる認可コードの漏洩
- リダイレクトエンドポイントにリダイレクトしたあとの画面から攻撃者のサイトに遷移した場合、リファラの URI から認可コードを取得する手口
- コンフィデンシャルクライアントの場合は、一度安全なところにリダイレクトさせると対応できる
- パブリッククライアントの場合は、
response_mode
でfragment
を指定することで対応できる
認可サーバの混同
- パターン1
- 複数の認可サーバを選べる状態で、かつリダイレクトエンドポイントを1つしか用意していない場合、悪意のある認可サーバが正当な認可サーバの認可レスポンスを自分にリダイレクトさせ認可コードを盗む手口
- リダイレクトエンドポイントを認可サーバ毎に用意することで対応できる
- パターン2
- 攻撃者が事前に正当な認可サーバで認可コードを取得しておき、悪意ある認可サーバにアクセスしてしまったユーザに正当な認可コードを正当なサーバでその使うようにリダイレクトレスポンスし、攻撃者が正当にログインする手口
- クライアントが、選んだ認可サーバを記憶しリダイレクト前にチェックすることで対応できる
この章の参考資料
- 雰囲気で使わずきちんと理解する!整理してOAuth2.0を使うためのチュートリアルガイド ( amazon )
- OpenID Connect入門: 概念からセキュリティまで体系的に押さえる ( amazon )
- アプリ開発で知っておきたい認証技術 - OAuth 1.0 + OAuth 2.0 + OpenID Connect -
- 一番分かりやすい OAuth の説明 - Qiita
- OAuth 2.0 クライアント認証 - Qiita
-
myapp://
とかのこと 例えばブラウザでdict://apple
にアクセスすると辞書アプリが起動する ↩︎