Skip to content

Instantly share code, notes, and snippets.

@SpringMT
Forked from yuua/oauth2_fugu_book.md
Last active August 29, 2015 14:12
Show Gist options
  • Save SpringMT/faedbdf689748a307b43 to your computer and use it in GitHub Desktop.
Save SpringMT/faedbdf689748a307b43 to your computer and use it in GitHub Desktop.

1章

重要な用語

1.認証(Authentication) ユーザ自身が何者であると主張しているかを検証するプロセス ユーザ名が表すのはユーザが主張するアイデンティティであり、アプリケーション側は、ユーザの入力したパスワードが正しければ、本人であるとみなす

2.連合型認証(Federated Authentication) ユーザアイデンティティの検証プロセスを外部サービスに依存しているアプリケーションのことをいう。 OpenIDなどが有名(OpenIDプロバイダのGoogleとかYahoo!とか)

3.認可(Authorization) 何らかの行為を行う際に、ユーザにその権限があるかどうかを検証するプロセス。 webアプリケーションは最初にログインしているIDを確認したあと、各操作に対するアクセスコントロールリストを参照して、そのアクセスが許可されている範囲のデータとサービスに対するものかを確認する。

4.委譲認可(Delegated Authorization) 他人やアプリケーションに自分に代わってアクションを実行してもらうためにアクセスを与えること。 ユーザはアプリケーションにアクセス権限を与え、ユーザのためにアクションを実行してもらう。ただし、アプリケーションが実行できるのは認可されたアクションのみ(scope的なノリ?)

5.ロール(Roles) OAuthプロトコルに手順に登場する主な動作主体

  • リリースサーバ OAuthによって保護されたユーザ取得リソースを保持、提供するサーバAPIプロバイダとかがこれを指す

  • リリース所有者 アプリケーションのユーザ。リソースサーバが保持するジズからのデータに対するアクセスを許可する

  • クライアント リソース所有者の認可を受け、保護されたリソーに対して何らかのアクションを起こすよう所有者に代わってAPIにリクエストを行うアプリケーション(コンシューマのアプリ的な感じ)

  • 認可サーバ リソース所有者の同意を得て、kライアンとにリソースサーバ上の保護されたリソースに対するアクセストークンを発行する。

MAC鍵

MAC鍵について

MAC鍵はhmac-sha-1 か hmac-sha256のアルゴリズムで生成されたものでなければいけない 署名が必須なOAuht対応APIへの接続では全てのAPIリクエストのAuthorizationヘッダにMAC署名が含まれていなければいなけない

MAC鍵の発行

OAuth認可サーバ自身で発行 access_tokenが認可サーバから返されるタイミングで毎回鍵も帰ってくる。 または、開発者がAPIproviderにアプリケーションを登録するときにMAC鍵がAPIリクエスト時以外の別プロセスで発行される場合もある。どんな方法で発行された鍵でも SSL/TLSチャネルによって機密に保たれていないといけない

APIリクエストの生成

署名が必須なOAuth対応APIへの接続では、全てのAPIリクエストのAuthorizationヘッダにMAC署名が含まれていないといけない。 署名を作成するにはリクエスト文字列(認証用乱数、HTTPメソッド、リクエストURI、ホスト名、ポート番号、ボディのハッシュ値など)を正規化して、暗号化しないといけない。

事前の登録

OAuthアプリケーションを登録するとクライアントクレデンシャルが発行される

  • クライアントID
    リソースサーバと通信する際のclient_idの値として指定

  • クライアントシークレット 認可コードをアクセストークン、リフレッシュトークンと交換するときにclient_secretの値として指定

クライアントクレデンシャルは認可コードからアクセストークンへの交換や、アクセストークンの更新などを行う際に、それらのリクエストの信頼性を守るために使われる。

ベアラートークン

保護されたリソースへのアクセスで必要となるトークン値だけが含まれたアクセストークン

アクセストークン

OAuth2.0を利用するAPIの多くがリクエスト認可で必須としているのはベアラートークンのみ。ベアラートークンを使った認可では、APIリクエストを作成するときに暗号鍵のような追加情報を含める必要がない OAuthを使う場合は全て同じアクセストークン取得して、APIリクエストを実行すること アクセストークを取得したらそのトークンをAPIリクエストとともに送る。 最適な方法は HTTPのAuthorizationヘッダを使うこと。

GET /unko/v1/product/@default/list HTTP/1.1 Host:www.unko.com Authorization:Bearer unko.hash

authorizationヘッダを使うのが優れている理由は

  • ヘッダはプロキシサーバやwebサーバのアクセスログにログとして残ることがほぼない

  • ヘッダがキャッシュされることがほぼない

  • クライアントがリクエストを行うときに、ヘッダはブラウザキャッシュに残らない

OAuth2.0では他の方法も定義されているが、実装するかはプロバイダが決めること。

他の方法 1.クエリパラメータ access_tokenをURLクエリパラメータに追加する方法。デバック時などは便利、クライアントサイドフローを使っている場合はJSONPのリクエスト形式のためこの方法が役に立つ

https://www.unko.com/unko/v1/product/@default/list?callback=outputTasks&access_token=unko.hash こんな感じ

2.フォームエンコードされたボディーパラメータ アプリケーションがAuthorizationヘッダを変更できない場合の手段。HTTPボディをHTTPボディにapplication/x-www-form-urlencodedコンテントタイプのパラメータを追加できる場合のみ使える

認可フロー

OAuth2.0プロトコルでは、認可を得るために使われる4つの基本的な「グラントタイプ」(認可供与方式)と、拡張方法が定義されている

1.認可コード リソース所有者がデータへのアクセスを認可すると、その後、webアプリケーションにリダイレクトされるが、URLのクエリパラメータ軽視この認可コードが渡される。クライアントアプリケーションではこの認可コードをアクセストークンに交換する。交換の際にはclient_idclient_secretが必須。また、リフレッシュトークンを使って APIへのアクセスを長期にわたって可能にすることができる。

2.インプリシットグラント(ブラウザベースのクライアントサイドアプリケーション用) ブラウザで動作するクライアントサイドwebアプリケーション用に特化されている。リソース所有者がアプリケーションにアクセスすると即座に新しいアクセストークンが生成され、URLのハッシュフラグメントを使ってアプリケーションに送られる。jsなどを使ってハッシュフラグメントからアクセストークンを取得してAPIを実行。認可コードは不要だがリフレッシュトークンは使えない。

3.リソース所有者パスワードクレデンシャル リソース所有者のユーザ名/パスワードがOAuhtアクセストークンと交換できる。APIプロバイダ自身が開発したアプリケーションなどの信用できるクライアントでのみ使われる。一度認証が終われば、あとはOAuthトークンだけおw保存しておけばオッケー。

4.クライアントクレデンシャル アプリケーション自身の所有するリソースに対しアクセストークンを取得する場合、または、認可サーバとの事前のやりとりによって、すでに認可を得ている場合に使用する。特定のユーザではないので、ストレージサービスやデータベースなどのAPIアクセスが必要な場合に適している。

以下は追加的なフロー

5.デバイスプロファイル 入力方法に制限あがあるデバイスでOAuthを使用するために作られたもの。 Facebookhがフローの実例を紹介してるよ☆ http://oauth-device-demo.appspot.com/

6.SAMLベアラーアサーションプロファイル SAML2.9アサーションをOAuthアクセストークンに交換できる。

フグ本OAuthについて(第二章)

2章

webアプリケーションフロー(認可コードフロー)

リソース所有者が、アプリケーションによってAPIプロバイダのOAuth認可サーバへリダイレクト。リダイレクト先の認可サーバはユーザがアクティブなセッションを持ているかどうかを確認。その後認可サーバが要求データに対するアクセウを認可するように促す。ユーザはアクセス認可すると最初のwebアプリケーションにリダイレクトで戻されるが、URLにはcodeクエリパラメータとして認可コードが付加されている。 codeクエリパラメータとして渡されるため、webブラウザからOAuthクライアントであるwebサーバにも送られる。この認可コードがwebサーバと認可サーバ間のやりとりで使用するアクセストークンと交換される。クライアントがAPI呼び出しを行う際にこのアクセストークンが使われる。

セキュリティ特性

アクセストークンはリソース所有者のブラウザからみえることはない。認可を行うために認可コードが使われるがこれはブラウザを通して受け渡される。保護されたAPIを呼び出す際は認可コードをアクセストークンに変換しておかなければならない。この変換プロセスはリクエストと共に、client_secretが渡された場合のみ成功する。このため、クライアンのセキュリティが守られている場合はアクセストークンの機密性が確保できる。アクセストークンの機密性はリソース所有者に対しても守られる。つまり、アクセストークンを使って生成されたAPIリクエストは、クライアンとその開発者の直接的な管理下にあるということ。 アクセストークンはブラウザを介していないので、履歴、refererヘッダ、jsなどから漏洩するリスクを軽減できる。 アクセストークンの漏洩リクスは小さいが、このフローを利用するアプリケーションの多くが、データベースあキーストアに有効期限の長いリフレッシュトークンを保持しておき、データへのオンラインアクセスを実現するが、アプリケーションが長期に渡るオフラインアクセスを要求するとさらなると、多数のユーザデータに対し、攻撃され得るアクセスポイントを一箇所に集約した状態で持つことになるのでリスクが生まれる。この問題はクライアントサイドwebアプリケーションフローのような他のフローでは存在しない。このようなリスク増加があっても、構造上、新しいアクセストークンを得るためのにユーザのブラウザと通信するのが簡単ではないため、多くのwebサイトはオフラインデータアクセスを使用する。

もろもろステップ

APIプロバイダにアプリケーションを登録、OAuthクライントIDとクライアントシークレットを入手し、コードを書く。

1.ユーザにこれから実行する内容を知らせ、認可を求める 認可を得るためにAPIプロバイダのサイトにリダイレクトするので、ユーザにこれからどんな内容を実行するのか予め知らせておくべき。メッセージを表示して、 add tasks to your unko みたいなのをつけてくとか

エラー処理 リクエストパラメータに無効なものが含まれていた場合、エラー状態となる。 redirect_uri,client_id、その他のリクエスト情報に問題があった際は、認可サーバはユーザにエラーメッセージを表示し、アプリケーションへのリダイレクトを中止する ユーザがアクセス要求を認めなかった場合もエラー応答が生成され access_denied型のエラーを表すパラメータと共にredirect_uriにリダイレクトされる。認可サーバはerror_description(エラー情報メッセージ)、error_uri(エラー情報を掲載したページのURL)などを送ることもできる。 OAuth2.0の仕様では下記のエラーが定義されている

  • invalid_request リクエストに必要なパラメータ不足、サポート外の値指定、その他の不正な形式

  • unauthorized_client 認可コード要求が認められていないクライアントからのリクエスト

  • unsupported_response_type 認可サーバがサポートしていない形式で認可コードをの取得をした

  • invalid_scope したいされたスコープが無効/未定義/不正な形式

  • server_error 認可サーバで想定外のエラーが発生し、リクエストを実行できない

  • temporarily_unavailable 一時的な高負荷などにより処理できない

2.認可コードをアクセストークンに交換する 認証プロセスにエラーがなければ、認可サーバはユーザをredirect_urlで指定されたURLにリダイレクトする。ユーザがアクセスを認証した場合、webアプリケーションにリダイレクトで戻る差異、2つのクエリパラメータが付加される

  • code 認可コード。ユーザがアクセス要求を承認したことを示す

  • state 認可サーバに最初にリクエストを送った時に渡したstateパラメータの値

このstate値を最初に作成sた値と比較し、一致しなければCSRF攻撃の可能性がある。この場合はOAuthを中断すべき。 送られてきたコードをAPIリクエストに使用するOAuthアクセストークンに交換する必要があるが、ライブラリなどを使用しない場合は、トークンエンドポイントに対するHTTP POSTリクエストをじぶんで 生成する。今回の場合、生成には下記パラメータが必要

  • code アプリケーションに渡された認可コード

  • redirect_uri リダイレクトURI。あらかじめ登録された、認可エンドポイントへの最初のリクエスト時に指定した場所

  • grant_type グランとタイプ。authorization_codeという値を指定する。認可コードをアクセストークンに交換することを示す。

このHTTP POSTリクエストはアプリケーション等r9時に与えられたclient_idとclient_secretによる認証を受けなければならない。OAuth2.0の仕様には、リクエストを認証する方法が主に2種類定義されている。 AuthorizationヘッダによるHTTP Basic認証(client_idをユーザ名、client_secretをパスワード)を利用する方法とclient_idとclient_secretをHTTP POSTパラメータに追加する方法

Authorizationヘッダの場合は Authorization: Basic MDAwMDAwMDA0NzU1REU0MzpVRWhrTDRzTmVOOFlhbG50UHhnUjhaTWtpVU1nWWlJNg

HTTP POSTパラメータの場合はcode、stateと一緒に下記パラメータが必要

  • client_id クライアントID。アプリケーション登録時に割り当てられたID

  • client_secret クライントシークレット。アプリケーション登録時に割り当てられた秘密の文字列。

リクエストの認証が終わり、パラメータが適切な場合は、認可サーバからのレスポンスとしてOAuthアクセストークンを生しjsonで返す。

  • access_token APIリクエストを認可するときに使用するトークン

  • token_type 発行されたアクセストークンの種類。大体「bearer」

  • expires_in

アクセストークンの有効期限の残り秒数

  • refresh_token リフレッシュトークン。現在のアクセストークンが死んだ際に新しいアクセストークンを取得するために使うトークン

アクセストークンとリフレッシュトークンが必要な理由

OAuth2.0では通常ベアラートークンが使われるが、保護されたAPIサービスがセキュリティ上危険になると、クライアントから受け取ったアクセストークンが攻撃者にさらされることになりかねない。OAuthでは、複数の異なるAPIに対するアクセスをアプリケーションに与える場合も考えられる。そうなると1つのサービスが危険になった場合に他のサービスも影響を受ける可能性がある。有効期限が短いアクセストークンだけがAPiサービスにアクセスできるようなっていれば、攻撃された際の影響範囲を狭めることができる。

APIサービスはクライアントからアクセストークンを受け取った時に、要求するアクセスに対して、正しいトークンかどうか確認する必要がある。受け取ったトークンが自分で検証できない場合はAPIサービスのOAuth認証サービス内部へのリクエストを行うか、データベースを参照しトークンの有効性が判断される。ただ、これによってAPIリクエストに対し遅延が発生する可能性があるので、OAuthの代わりにアクセストークンとして署名付き文字列や暗号化文字列を使うプロバイダもある。

APIの呼び出し

ベアラートークンが使われている場合は、アプリケーションからのAPIリクエストが認可済みであることを示す際に、リクエストにアクセストークンを含めるだけでオッケー。デジタル署名は要らない。 (アクセストークンの送付はやっぱりAuthorizationヘッダがいいよ)

アクセストークンの更新(リフレッシュ)

トークンのエンドポイントに対し grant_typeとしてrefresh_tokenを指定し、refresh_tokeんを付加したHTTP POSTを実する。このリクエストに対しても認証は必要

アクセス権限の取り消し

アカウントの管理インターフェースで明示的にアクセス取り消しを指定してもうらう方法が一般的。Facebookとかはパスワードをユーザが変更したら即無効になるらしい。 googleとかはリフレッシュトークンとかの取り消し用のプログラムを用意していて、それを叩くと無効にできる。

フグ本OAuthについて(第三章)

3章

インプリシットグラントフロー

ユーザが認可要求を承認すると、即座にアプリケーションにアクセストークンが返される。

インプリシットグラントフローを使うべきケース

  • 一時的なアクセスのみ

  • ユーザが日常的にAPIプロバイダにログインしている時

  • OAuthクライアントがブラウザで実行されている時(JSとかFlashとか)

  • ぶらず兄対する信用度が高く、アクセストークンが信頼出来ないユーザやアプリケーションに流出する期限が限定されている時

インプリシットグラントフローの制限

このフローではリフレッシュトークンは使われない。認可サーバがアクセストークンの期限を短く設定している場合、アプリケーションは都度都度認可フローを実施しなければならない。 プロバイダの中には過去に同じスコープを承認したことがあれば、承認画面を表示しないプロバイダもある。

セキュリティ特性

アプリケーションが有効期限の長いリフレッシュトークンをサーバに保存することがないので、サーバに侵入されてもリスクが限定的。また、クライアントのアクセストークンを更新するにはAPiプロバイダの認可サーバにてユーザ認証を受ける必要があるので、アクセストークンが流出した際にOAuht実装に応じた有効期限で失効することが保証されている。 ただ、アクセストークンがユーザのwebブラウザに直接送られるので、認可コードフローに比べアカウンタビリティの面で劣る。また、サードパーティのアプリケーションいよって生成されたようみ見えているAPI呼び出しが実際はリソース所有者によって直接生成された可能性もある。

ユーザエクスペリエンス

ユーザエクスペリエンスはサーバサイドアプリケーションと全く同じ

アクセス権限の取り消し

インプリシットグラントフローでもサーバサイドwebアプリケーションと同様のフロー

フグ本OAuth2について(第四章)

4章

リソース所有者パスワードクレデンシャルフロー

ユーザ名とパスワードをアクセストークン/リフレッシュトークン(オプション)と交換して使う。セキュリティ上このフローは他のOAuthフローとは異なる性質を持つ。大きな違いは、アプリケーションからユーザのパスワードにアクセスできるということ。そのため、このフローではアプリケーションに対する信頼が必要。

リソース所有者パスワードクレデンシャルフローを使うケース

APIプロバイダが自社でリリースするオフィシャルアプリ内のみで使うことが推奨されている。通常はサードパーティでの使用は認められていない。

セキュリティ特性

アプリケーションからリソース所有者のパスワードにアクセスはできるが、ユーザ名とパスワードを直接使って(HTTP Basic認証など)API呼び出しを行うよりは、このフローを使ったほうが多少はセキュリティ上優れている。Basicい認証の場合はAPIを呼び出すたびに、アプリケーションが毎回ユーザのパスワードにアクセスする必要があり、1つのアプリケーションのユーザデータがアクセスを取り消したい場合、パスワードを変更して他のすべてのアプリケーションに新しいパスワードを設定しなければならない。 リソース所有者パスワードクレデンシャルフローを使えば、アプリケーションがユーザクレデンシャルにアクセスするのは一度だけで十分で、その1回でアクセストークンに交換されるのでクレデンシャルをアプリケーションないで保存する必要もない。

ユーザエクスペリエンス

このフローはパスワードを使ったアクセス要求方法と同じ。アプリケーションがユーザ名とパスワードを要求し、ユーザが入力。次にアプリケーションがサーバサイドまたはクライアントサイドでAPIプロバイダの認可サーバに対するリクエストを生成。

フグ本OAuth2について(第五章)

5章

クライアントクレデンシャルフロー

クライアント自身がデータを所有していて、リソース所有者からのアクセス譲渡が不要な場合に使われるフロー このフローはOAuht1.0の2-leggedフローと同じような事例に適合するフロー。

クライアントクレデンシャルフローを使いべきケース

リソース所有者がアプリケーションに対して通常OAuthフロー以外の手段で自分のリソースにアクセス権限を与えている場合。

クライアント認証の方法

「クライアントが認可サーバによって適切に認証できるとこ」「認証クレデンシャルの機密性が守られていること」の2点が重要。クライアントが認証を受けるには、認可サーバにclient_idとclient_secretを送ればいいが、アクセストークン要求時にPOSTパラメータとして送る方法とHTTP Basic認証のAuthenticationヘッダを使って送る方法がある。その他にも 公開鍵/秘密鍵のペアを使う方法やSSL/TLSクライアント認証による方法などもある。

セキュリティ特性

クライアントクレデンシャルフローでは一組のクライアントクレデンシャルで大量のデータにアクセスできる。一組のクライアントクレデンシャルからアクセスできるデータ量が大きくなるほど、漏洩のリスクは高まる。なので、クライアントの認証用のクレデンシャルは漏洩しないように管理することが重要。

認証ステップ

ex.facebookのApp Login機能

ステップ1 アプリケーションのクレデンシャルをアクセストークンへ交換

アプリケーションから認可サーバへアクセストークンリクエストを送る。このリクエストにはクライアントクレデンシャルによる認証が必要。 POSTパラメータに必要なもの

  • grant_type client_credentialsを指定

  • client_id アプリケーションを登録した時に割り当てたれた値

  • cliet_secret アプリケーション登録時に割り当てられた値

クライアントクレデンシャルにおる認証が終わると、クライアントにアクセストークンが返される。Facebookでは、access_tokenがURLエンコードされ、レスポンスボディに含まれた形で返される。

ステップ2 API呼び出し

クライアントクレデンシャルフローで発行されるOAuthアクセストークンは、その他のフローで発行されるものと同じなので、使用方法も同じ。APIのプロバイダのサポート状況にあわせて、HTTP Authoraizationヘッダ、またはクエリパラメータの値としてアクセストークンを渡す。

アクセストークンの有効期限

クライアントクレデンシャルでは、有効期限の長いアクセストークンが発行される。仕様ではリフレッシュトークンの発行はサポートされていないなので、有効期限が切れた場合は新しくアクセストークンを要求し直す。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment