account_web に携わるにあたって必要になるとのことで、 OAuth2.0 について調べたことのまとめです。
今回のまとめで触れるもの、触れないもの。
今回のまとめにおいて、主に触れるのはOAuth2.0のプロトコルフローについてです。 なお、プロトコルフローは下記のような6ステップに分けることができますが、今回触れるのは1 ~ 4 までとなります ( 5以降は accout_web の仕事ではないため ) 。 1. クライアントが認可を要求 1. クライアントが認可グラントを受け取る 1. クライアントが認可グラントを提示 ( アクセストークンの要求 ) 1. 認可サーバーがクライアントを認証 ( アクセストークンの付与 ) 1. クライアントが保護リソースへアクセス ( アクセストークンでの認証 ) 1. リソースサーバーがアクセストークンを確認 ( リクエストの受け入れ )
中でも、認可グラントの受け取り ( 2 ) については認可コードフローのみについてまとめています。
従来のクライアントサーバー型認証やOAuth1.0, クライアントクレデンシャル、インプリシット、リソースオーナーパスワードクレデンシャルについては今回触れません。
登場人物紹介 ( 用語集 )
もうすでに重要な登場人物が出てきていますが、ここで改めて登場人物の紹介を行います。 仕様書を読んだ個人の主観に基づいたざっくりな説明です。 ##リソースオーナー ( Resource Owner ) 保護リソースへのアクセスを許可するエンティティ ( 人間だったらエンドユーザーと呼ばれる ) 。 この人がリソースを持っているので、それがほしい人はみんなこの人に直接「ちょうだい!」って言う ( 認可要求する ) のかと思いきやそうではない ( できるけど望ましくない ) 。
保護リソース ( Procted Resource )
リソースオーナーが持っている、アクセスが制限された情報。 OAuth 認証をすることで取得可能になる。 クライアントが欲しがっているもの。
クライアント ( Client )
保護リソースが欲しい人。 リソースオーナーの認可を得てリソースオーナーの代理として保護リソースに対するリクエストする。
認可サーバー ( Authorization Server )
リソースオーナーの認証とリソースオーナーからの認可取得が成功した後、アクセストークンをクライアントに発行するサーバー 。
認可グラント ( Authorization Grant )
リソースオーナーが保護リソースへのアクセスを行うことを認可したことを示す。 クライアントがアクセストークンを取得する際に用いられる。
アクセストークン ( Access Token )
クライアントがリソースオーナーに代わって認証済みリクエストを行うためのトークン。 クライアントに対して発行される文字列。クライアントから見るとランダムな文字列。 認可情報を取り出すための識別子や、認可情報を含んでいることもある。 アクセストークンによって様々な認可要素をリソースサーバーが解釈できる単体のトークンに置き換えるような抽象化レイヤーが提供される。これによって、認可グラントよりも制限の強いアクセストークンの発行が可能になったり、数多くの認証方式をリソースサーバーが解釈する必要性がなくなったりする。
認可コードフロー ( Authorization Code Flow)
クライアントは、リソースオーナーへ直接認可を要求する代わりにユーザーエージェントを経由してリソースオーナーを認可サーバーへリダイレクトさせ、リソースオーナーがリダイレクトして戻ってきた際に認可コードを取得する。 リソースオーナーを認可コードとともにリダイレクト経由でクライアントに戻す前に、 認可サーバーはリソースオーナーを認証し認可を得る。 これによりリソースオーナーは認可サーバーによってのみ認証され, リソースオーナーのクレデンシャルはクライアントへ共有されない。 クライアントを認証する機会を提供したり、リソースオーナーのユーザーエージェントを経由せずアクセストークンをクライアントに送信できるなど、セキュリティ的なメリットがある。
認可コード ( Authorization Code )
アクセス許可。生存期間が短い。 アクセストークンと ( リフレッシュトークン ) を得るために使われる。
クレデンシャル ( Credential )
IDやパスワードをはじめとしたユーザー認証に用いられる情報。
リフレッシュトークン (Refresh Token )
クライアントがリソースオーナーの新しいアクセストークンを得るためのトークン。
TLS ( Transport Layer Security )
通信相手の認証、通信内容の暗号化、改竄の検出を提供するプロトコル。
認可エンドポイント(Authorization Endpoint)
認可サーバーのHTTPエンドポイント。 クライアントがユーザーエージェント経由でリソースオーナーから認可を得るために利用する。
トークンエンドポイント ( Token Endpoint )
認可サーバーのHTTPエンドポイント。トークンの発行とリフレッシュを行う。 クライアントが認可グラントとアクセストークンを交換するために利用する。
リダイレクトエンドポイント ( Redirection Endpoint )
クライアント上のHTTPエンドポイント。
クライアント登録
OAuth プロトコルフローを開始する前に、クライアントは認可サーバーに登録します。登録方法についてここでは詳しく触れませんが、通常はHTML登録フォームを持ちます。 クライアント登録はクライアントと認可サーバーの直接的なやり取りでおこなわなければならない訳ではありません。認可サーバーでサポートされているのであれば、クライアントのプロパティを取得し、新楽を確立する他の方法を用いて登録を行うこともできます。 クライアントを登録する場合、クライアント開発者は以下を満たすものとします。 - クライアントタイプの説明で説明されているようなクライアントタイプを指定すること - リダイレクトエンドポイントの説明で説明されているようなリダイレクトURIを提供すること - 認可サーバーが要求する情報を提供すること
クライアントタイプ
認可サーバーと安全に認証するの右翼に基づいて、OAuth はふたつのクライアントタイプを定義しています。 - コンフィデンシャル:クレデンシャルの機密性を維持することができるか、他の手段を使用したセキュアなクライアント認証ができるクライアント。 Webアプリケーションはこれに該当する。 - パブリック:クライアントクレデンシャルの機密性を維持することができず、他の手段を使用したセキュアなクライアント認証もできないクライアント。 ユーザーエージェントベースアプリケーションやネイティブアプリケーションがこれに該当する。
プロトコルエンドポイント
認可コードフローで認可をするときに使用するエンドポイントについての仕様です。
認可エンドポイント
以下の要件を満たさなければなりません。 - リソースオーナーのアイデンティティを確認すること - エンドポイントURIにクエリコンポーネントを含んでいる場合、クエリパラメータを追加するときは指定されたコンポーネントを維持すること - エンドポイントURIにフラグメントコンポーネントを含まないこと - 認可エンドポイントへのリクエストを送信するときに認可サーバーはTLSを利用すること - 認可サーバーは認可エンドポイントで HTTP GET メソッドをサポートすること - パラメーターの値がない場合はパラメーター自体が勝楽されているものとして扱うこと - 認可サーバーは識別できないリクエストパラメーターを無視すること - リクエスト及びレスポンスパラメータが重複しないこと
レスポンスタイプ
クライアントは response_type
パラメータを使用して希望するグラントタイプを認可サーバーに通知します。
今回は認可コードをリクエストするので code
である必要があります。
認可リクエストで response_type
がない場合、もしくは未知のレスポンスタイプであった場合、認可サーバーはエラーレスポンスを返さなくてはなりません。
リダイレクトエンドポイント
リソースオーナーとのやりとりが完了した後、認可サーバーはユーザーエージェントを認可リクエスト時に指定されたクライアントのリダイレクトエンドポイントにリダイレクトさせます。 また、リダイレクトエンドポイントは以下を満たす必要があります。 - 絶対URIであること - クエリパラメータを追加するときは指定されていたクエリコンポーネントを維持すること。 - フラグメントコンポーネントを含まないこと
【エンドポイントリクエストの機密性】
認可コードフローの場合はTLSを要求するべきです。 仮にTLSができない場合は、認可サーバーはリダイレクト前にリソースオーナーに対して決して安全でないエンドポイントであることを警告するべきです。
【登録要件】
認可サーバーはパブリッククライアントに対してリダイレクトエンドポイントの事前登録を要求しなくてはなりません。 認可サーバーは以下のことについて満たすべきとされています。
- 全てのクライアントに対して、認可エンドポイントアクセス前にリダイレクトURIの事前登録を要求すること
- クライアントに完全なリダイレクトURIの等ロックを要求すること
- 蒸気が不可能な場合はURIスキーム、オーソリティ、パスの登録を要求すること
リダイレクトURI登録がない場合は、攻撃者に認可エンドポイントをオープンリダイレクターとして利用されてしまう恐れがあります ( 下記を参照 ) 。
【オープンリダイレクタ】
正統性の確認をせずに、パラメータで指定されたURLにユーザーエージェントを自動でリダイレクトするエンドポイントのことです。 これはフィッシング攻撃やエンドユーザーを悪意あるサイトに誘導するのに利用されます。 さらに、認可サーバーがクライアントにリダイレクトURIの一部のみで登録を許可した場合は攻撃者は認可サーバーの正統性確認を通過し、認可コードやアクセストークンを利用できるようになります。
【動作設定】
認可サーバーは以下の動作設定を行う必要があります。
- リダイレクトURIが複数あったり、一部のみだったり、そもそも登録されていない場合、クライアントは
redirect_uri
リクエストパラメートを使って認可リクエストにリダイレクトURIを含むこと - リダイレクトURIが事前登録されていて、認可リクエスト中に含まれていた場合は認可リクエストに含まれる値を登録済みのリダイレクトURIと比較して少なくとも1つと一致することを確認すること
- 登録にフルのリダイレクトURIが含まれていた場合は単純な文字列比較で比較すること。
【無効なエンドポイント】
リダイレクトURIの欠落、無効、不一致のため認可リクエストが失敗した時は、認可サーバーはエラーをリソースオーナーに知らせるべきです。 また、無効なリダイレクトURIにユーザーエージェントを自動でリダイレクトしてはいけません。
【エンドポイントコンテント】
クライアントは以下を満たすべきです。
- リダイレクトエンドポイントのレスポンスにサードパーティーのスクリプトをインクルードしないこと
- URIからクレデンシャルを抽出し、クレデンシャルを露出することなくユーザーエージェントを再び別のエンドポイントへリダイレクトすること
- サードパーディのスクリプトがインクルードされていた場合、クライアントは地震のスクリプトが最初に実行されることを保証すること
トークンエンドポイント
トークンエンドポイントは、認可グラントもしくはリフレッシュトークンをアクセストークンと交換するためにクライアントに利用されます。 トークンエンドポイントは以下のことを満たしている必要があります。
- クエリパラメータを追加する際、指定されていたクエリコンポーネントは維持すること
- フラグメントコンポーネントを含まないこと
- 認可サーバーはリクエストにTLSを必須とすること ( クレデンシャルが平文で転送されるため )
- クライアントはアクセストークンリクエストを送信する際に HTTP POST メソッドを使用すること
- 空の値で送信されたパラメータは省略されたものとして扱うこと
- 認可サーバーは未知のリクエストパラメータを無視すること
- リクエスト及びレスポンスパラメータは重複を許さないこと
クライアント認証
コンフィデンシャルクライアントは下記にしたがってトークンエンドポイントへのリクエスト時に認可サーバーとの間でクライアント認証を行う必要があります。
- 認可サーバーのセキュリティ要求を満たすクライアント認証方式を確立すること
- 1回のリクエストにおいて複数の認証方式を利用しないこと
- クライアントパスワードを保持しているクライアントは HTTP Basic 認証スキームを使用してもいい
- HTTP Basic 認証スキームの代わりに
client_id
とclient_secret
を用いても良い ( 非推奨。Basic 認証が不可の場合に限定すべき ) - パスワード認証を行う場合はTLSの利用が必須とすること
- トークンエンドポイントへのリクエスト時に、認証を行わないクライアントはリクエストに
client_id
を含むこと
クライアント認証には以下のような目的があります。
- リフレッシュトークンや認可コードとクライアントの紐付けを強化すること
- セキュアでないチャネルを通じて認可コードが送信された場合やリダイレクトURIが完全に登録されていない場合に重要
- 盗まれたリフレッシュトークンの濫用を防止するなど、何らかの攻撃に晒されたクライアントによる被害を抑えること。
- クライアントの無効化やクライアントクレデンシャルの変更を小なうことで素早く対応
- 定期的なクレデンシャルの更新が容易
アクセストークンのスコープ
クライアントは、認可エンドポイントとトークンエンドポイントで scope
というリクエストパラメータを用いてアクセス範囲を明示することができます。同様に、認可サーバーがクライアントにアクセストークンのスコープを通知するのにも scope
レスポンスパラメータを使用します。
スコープパラメータは
- 大文字と小文字を区別する文字列
- スペース区切りのリスト
- 文字列は認可サーバーによって定義される
- 順序に意味はない
- それぞれのアクセス範囲を足し合わせたものが要求される
ものです。
認可の取得
アクセストークンを要求するため、クライアントはまずリソースオーナカーから認可を取得します。 ここでは認可コードフローについてまとめています。
認可コードによる認可
認可コードグラントタイプは、アクセストークンとリフレッシュトークンの両方を取得するために用いられ、コンフィディンシャルクライアントに最適化されています。 クライアントはリソースオーナーのユーザーエージェント ( 通常はwebブラウザ ) と対話し、認可サーバーにさるリクエストを受け付ける必要があります。
① 認可リクエスト
クライアントは認可エンドポイントURIへのクエリとして次のパラメータを付与します。
- response_type
:必須。値は code
。
- client_id
:必須。クライアントが提供した登録情報を表すユニーク文字列。クライアント識別子。
- redirect_url
:任意。事前登録するか認可リクエスト時に指定する必要がある。
- scope
:任意。
- state
:推奨。リクエストとコールバックの間で状態を維持するためのランダムな値。認可サーバーはクライアントにリダイレクトする時にこの値を付与する。CSRFを防止する。
認可サーバーは必要なリクエストパラメータが全て揃っていて正当であることを確認します。リクエストが正当であれば、認可サーバーはリソーソーナーを認証し、認可の判定を得ます。
② ③ 認可レスポンス
リソースオーナーがアクセスリクエストを許可したら、認可サーバーは認可コードを発行し、リダイレクトURIのクエリコンポーネントに次のパラメータを付与してクライアントに送ります。
- code
:必須。認可コードは認可サーバーによって許可される。クライアント識別子とリダイレクトURLに紐付く。
- 漏洩のリスクを軽減するため、認可コードは発行されてから短期間で無効にすること。
- 認可コードの有効期限は最大でも10分を推奨。
- クライアントは2回以上認可コードを使用してはならない。
- 認可コードが2回以上使用されたら認可サーバーはリクエストを拒否すること。
- 認可コードが2回以上使用されたらこの認可コードを基に発行されたこれまでの全てのトークンを無効化すべき。
- state
:認可リクエストに state
パラメータが含まれたいた場合は必須。受け取った値をそのまま返す。
クライアントは認識できないレスポンスパラメータを無視しなくてはなりません。
【エラーレスポンス】
リクエストがリダイレクトURIの欠落・不正・ミスマッチによって失敗した場合と、クライアント識別子が不正な場合は、認可サーバーはリソースオーナーにエラーを通知するべきです。不正なリダイレクトURIに対してユーザーエージェントを自動的にリダイレクトさせてはいけません。 リソースオーナーがアクセス要求を拒否した場合やリクエストURIの不備でリクエストが失敗した場合は、認可サーバーはリダイレクトURIのクエリコンポーネントに次のようなパラメータを付与してクライアントに返却します。
error
:必須。次のASCIIで表されるエラーコードの中の一つ。invalid_request
:リクエストに必須パラメータが含まれていない、サポート外のパラメータが付与されている、同一のパラメータが複数含まれている、その他不正な形式であった場合。unauthorized_client
:クライアントは現在の方法で認可コードを取得することを許されていない場合。access_denied
:リソースオーナーまたは認可サーバーがリクエストを拒否した場合。unsupported_response_type
:認可サーバーが現在の方法による認可コード取得をサポートしていない場合。invalid_scope
:リクエストスコープが不正、未知、その他不当な形式である場合。server_error
:認可サーバーがリクエストを処理できないような予期しない状況に遭遇した場合。temporarily_unavailable
:認可サーバーが一時的なカフカやメンテナンスによってリクエストを扱うことができない場合。
error_description
:任意。クライアント開発者が発生したエラーを理解する際の手助け。可読性のあるASCII文字列で表される追加情報。error_uri
:任意。クライアント開発者が発生したエラーに関する追加の情報を得ることができるWebページのURI 。state
:state
パラメータがクライアントからの認可リクエストに含まれていた場合は必須。受け取った値はそのまま返す。
④ アクセストークンリクエスト
クライアントはトークンエンドポイントに対して、次のようなパラメータを付与してリクエストを送信します。パラメータはHTTPリクエストのエンティティボディにHTF-8でそれぞれエンコードします。
- grant_type
:必須。値は authorization_code
。
- code
:必須。認可サーバーから受け取った認可コード。
- redirect_uri
:認可リクエストに redirect_uri
を含んだ場合は必須。認可リクエスト時と同じ値。
- client_id
:クライアント認証がされていなければ必須。
⑤ アクセストークンレスポンス
アクセストークンリクエストが正当で認可されたら、認可サーバーはアクセストークンと任意でリフレッシュトークンを発行します。リクエストが失敗もしくは不正だった場合はエラーレスポンスを返却します。
【成功レスポンス】
アクセストークン ( 及び任意でリフレッシュトークン ) を発行します。このレスポンスはエンティティボディに以下のパラメータを含み、HTTP ステータスコード 200 ( OK ) で返されます。
- access_token
:必須。認可サーバーが発行するアクセストークン。
- token_type
:必須。トークンのタイプ。大文字小文字を区別しない。
- 保護リソースにリクエストする時にアクセストークンを適切に用いるために必要な情報。
- expires_in
:推奨。アクセストークンの有効期限を表す秒数 ( s )。
- refresh_token
:任意。リフレッシュトークン。同じ認可グラントを用いて新しいアクセストークンを取得するのに利用される。
- scope
:アクセストークンのスコープ。
これらのパラメータは json 形式で HTTPレスポンスボディに含まれます。 クライアントはレスポンスに含まれる認識できない名前の値を無視しなければなりません。
【エラーレスポンス】
( 特に指定がない場合は ) HTTP ステータスコード 400 ( Bad Request ) を返します。このレスポンスには以下のパラメータが含まれます。
- error
:必須。以下の ASCII エラーコードより一つを設定する。
- invalid_request
:リクエストに必要なパラメータが含まれていない、サポートされていないパラメータがある、クレデンシャルが複数、クライアント認証メカニズムが複数、もしくは異常な値が設定されている場合。
- invalid_client
:クライアント認証に失敗した場合。
- invalid_grant
:提供された認可グラントまたはリフレッシュトークンが不正、有効期限切れ、失効、認可リクエストでリダイレクトURIと異なる、他のクライアントに発行したものである場合。
- unauthorized_client
:認証されたクライアントが当該のグラントタイプを利用するように認可されていない場合。
- unsupported_grant_type
:グラントタイプが認可サーバーによってサポートされていない場合。
- invalid_scope
:要求されたスコープが不正、未知、異常、リソースオーナーによって与えられた範囲を超えている場合。
- error_description
:任意。クライアント開発者が発生したエラーを理解する際の手助け。可読性のあるASCIIテキストの追加情報。
- error_uri
:任意。クライアント開発者が発生したエラーに関する追加情報を得ることができるWebページのURI
これらのパラメータは json 形式で HTTPレスポンスボディに含まれる。
アクセストークンの更新
認可サーバーがクライアントにリフレッシュトークンを発行していれば、クライアントはトークンエンドポイントに更新リクエストを送ることができます。以下のパラメータがUTF-8エンコードされてHTTPリクエストエンティティボディに加えられます。
- grant_type
:必須。refresh_token
がセットされます。
- refresh_token
:必須。クライアントに発行されたリフレッシュトークン。
- scope
:任意。アクセス要求のスコープ。
リフレッシュトークンは、一般的に有効期限の長いついあのアクセストークンを要求するクレデンシャルなので、発行されたクライアントと紐づきます。クライアントタイプがコンフィデンシャルの場合とクライアントクレデンシャルが発行されたクライアントは認可サーバーにより認証されなければなりません。 リクエストが有効で認可されたら、認可サーバーはアクセストークンを発行します。レスポンスの詳細は アクセストークンレスポンスを参照してください。
認可サーバーは新しいリフレッシュトークンを返しても良いのですが、次のことに従う必要があります。 - クライアントは古いリフレッシュトークンを破棄し、新しいリフレッシュトークンに取り替えること - 認可サーバーは古いリフレッシュトークンを無効化しても良い - 新しいリフレッシュトークンのスコープは、クライアントによってリクエストに含まれたリフレッシュトークンのスコープと同一であること。
セキュリティで考慮すべき事項 ( クライアント視点 )
クロスサイトリクエストフォージェリ ( CSRF )
攻撃者がユーザーエージェントに悪意あるURIを閲覧させることによって、信頼が確立されたサーバーへ接続させる手法です。 クライアントのリダイレクトURIに対するCSRF攻撃は、攻撃者が自身の認可コードやアクセストークンを紛れ込ませることが可能で、クライアントに犠牲者の保護リソースではなく攻撃者のリソースに紐づいたアクセストークンを使わせることができてしまいます ( 例えば、犠牲者の銀行口座情報を攻撃者の管理しているリソースへ保存することも可能です )。
クライアントは自身のリダイレクトURIに対してCSRF保護対策を導入しなければなりません。
一般的には、リダイレクトURIのエンドポイントへ送られた全ての要求に対して、要求とユーザーエージェントの認証状態を紐づけるための値を含めます ( 例えばユーザーエージェントを認証するために使うセッションクッキーのハッシュなど )。クライアントは認可要求の発行時、この値を認可サーバーへ伝搬するために state
リクエストパラメータを利用するべきです。
一旦エンドユーザーの認可が得られると、認可サーバーはユーザーエージェントを state
パラメータに含まれる要求されたバインド値と一緒にクライアントへリダイレクトします。
クライアントはバインド値とユーザーエージェントの認証状を付き合わせることによってリクエストの正統性を確認することができます。
この時使用されるバインド値は、推測する可能性が 2^(-128) 以下にしなければならず、2^(-160) 以下の値を含まなくてはなりません。また、ユーザーエージェントの認証状態はクライアント及びユーザーエージェントのみがアクセスできる状態に保たれなければなりません。