Skip to content

Oauth 2.0 public client

Ryo Ito edited this page Jun 25, 2013 · 12 revisions

Public Clientの扱い(既にAuthorization Code GrantをサポートしているServer編)


モバイルアプリとOAuthについての最新の考えはこちら https://github.com/ritou/r-weblife/wiki/OAuth-2.0-for-Mobile-App

下記のことを意識して書いているので、汎用的なものではなくなったかもしれない。

  • 既にAuthorization Code Grantをサポートしていて、Public Clientへの対応を検討しているServerはどのように機能を拡張していくべきか
  • Public ClientからのOAuth 2.0利用をどのように実装すべきか

Public Clientとは

OAuth 2.0 Specの2.1.Client Typesで説明されている。

Clients incapable of maintaining the confidentiality of their credentials (e.g., clients executing on the device used by the resource owner, such as an installed native application or a web browser-based application), and incapable of secure client authentication via any other means.

一言で「client_secretを安全に保管できない環境で動作するアプリケーション」などと説明されることが多い。
今回は下記のようなアプリについてまとめる。

  • JavaScriptで動作するアプリ
  • モバイル/ネイティブアプリ(OAuthの結果を認証に利用する)
  • モバイル/ネイティブアプリ(OAuthの結果を認証に利用しない)

JSアプリ(OAuthの結果を認証に利用しない) : Public Client + Implicit Grant

ここでは今は亡きTwitter @anywhereのようなものをイメージする。

登場人物

  • サービスA : OAuth 2.0に従いAPIアクセスを提供しているサービス。JS SDKも提供している
  • サービスB : JS SDKを用いてユーザーにサービスAのリソースにアクセスさせる(書き込みなど)。 特にサービスB自体はリソースアクセスの必要なし
  • ユーザー : サービスB上でサービスAの機能を使いたい人

シナリオ

サービスB自体はリソースアクセスの必要がないため、アクセストークン受け渡しにフラグメントを利用してサーバーにログが残らない Implicit Grantを選択する。

  • サービスBの開発者はSDKを呼び出す数行のコードを自らが管理しているサービスに張り付ける。このときclient_idを指定し、redirect_uriも事前にサービスAに登録してあるものを指定する
  • ユーザーアクションにより、OAuth 2.0のAuthorization Requestが送られる(response_type=token)
  • ユーザー同意の後、サービスAはredirect_uriにフラグメントとしてAccess Tokenを受け取る
  • リソースアクセス

課題

  • AccessToken置換攻撃への対策は必要? : 今回の例はバックエンドにAccessTokenを送りリソースアクセスしたり認証に利用したりしていないのでしていない。CSRF対策としてはstateパラメータを利用する
  • AccessTokenの有効期限の間しか使えないの?Token Refreshは不要? : ブラウザなら同意画面を出さないAuthorization Requestなどを用いてAccess Tokenを更新できるかも。OAuth 2.0の仕様に定義されてはいない。

JSアプリがOAuthの結果を認証に利用する場合

イメージができないが、バックエンドってのとJSが動いているサービスが同一であれば、Authorization Code Grantでやればいいと思う。

こんな例はあるのか?

  • サービスCの上でサービスBのJSアプリが動いている
  • サービスBのアプリはサービスAのOAuthの結果をサービスBのバックエンドに送って認証* サービスBのアプリはサービスCのコンテンツをサービスAのタイムラインに流す

モバイルアプリ/ネイティブアプリ(OAuthの結果を認証に利用する) : Confidential Client + Authorization Code Grant

登場人物

  • サービスA : OAuth 2.0に従い APIアクセスを提供している サービス。iOS/Android SDK(公式アプリ使ってたり使ってなかったり)も提供している
  • サービスD : SDKを用いてユーザーにサービスAのリソースにアクセスさせるアプリケーションを開発。 バックエンドのWebサーバーと連携してOAuthの結果を認証に利用している
  • ユーザー : サービスDのアプリを利用

シナリオ

JSアプリとは異なり、カスタムURIスキームにはクエリパラメータがついていても特に問題にならない=フラグメント必須という条件がなくなると考えられる。
Implicit Grant + バックエンドにAccess Token送って認証するよりは、もうバックエンドにAuthorization Codeを送って処理を任せればいい。

  • サービスDの開発者はサービスAのSDKを自らが管理しているアプリに導入する。このときclient_idを指定し、redirect_uriも事前にサービスAに登録してあるものを指定する。
  • ユーザーアクションにより、サービスDのアプリからOAuth 2.0のAuthorization Requestが送られる。(response_type=code)
  • このとき、バックエンドのWebサーバーに問い合わせてアプリ-Webサーバー間でセッション的なものが張ってあり、Webサーバー側でそのセッションに紐づけたstateパラメータを払いだして管理しておけると後の処理が楽
  • アプリはWebサーバーから受け取ったstateパラメータをAuthorization Requestに含む
  • ユーザー同意の後、サービスAはサービスDのredirect_uri(アプリ)にクエリパラメータとしてcode(Authorization Code), stateを付加する
  • サービスDのアプリはSDKの関数からもらったcode, stateをバックエンドのWebサーバーに送信
  • サービスDのWebサーバーは保存してあるclient_secretを用いてAccess Tokenに交換し、Profile APIなどをたたいてユーザー認証を行う
  • このとき、Webサーバー側が保存しているstateパラメータとの突合せを行う(CSRF対策)
  • サービスDのアプリはその結果(と必要ならばAccess Token)をもらう
  • APIアクセスはWebサーバーに任せて、アプリにAccess Tokenを渡さない方がいいとも思う

課題

  • カスタムURIスキームの信頼性 : 同じ値を利用するアプリがいたらどうなるか、などの解はまだ持っていない

モバイルアプリ/ネイティブアプリ(OAuthの結果を認証に利用しない) : Public Client + Authorization Code Grant

登場人物

  • サービスA : OAuth 2.0に従い APIアクセスを提供している サービス。iOS/Android SDK(公式アプリ使ってたり使ってなかったり)も提供している
  • サービスE : SDKを用いてユーザーにサービスAのリソースにアクセスさせるアプリケーションを開発。 バックエンドにTokenを送って認証に利用していない むしろ、バックエンドがない!ぐらいの勢い。
  • ユーザー : サービスEのアプリを利用

シナリオ

JSアプリとは異なり、カスタムURIスキームにはクエリパラメータがついていても特に問題にならない=フラグメント必須という条件がなくなると考えられる。

既にAuthorization Code GrantをサポートしてるServerを想定しているため、ここではclient_secretなしのAuthorization Code Grantを利用する。

  • サービスEの開発者はサービスAのSDKを自らが管理しているアプリに導入する。このときclient_idを指定し、redirect_uriも事前にサービスAに登録してあるものを指定する。
  • ユーザーアクションにより、OAuth 2.0のAuthorization Requestが送られる。(response_type=code)
  • ユーザー同意の後、サービスAはサービスEのredirect_uriにクエリパラメータとしてcode(Authorization Code), stateを付加する
  • SDKはstateパラメータをチェックし、Access Tokenを要求する(code, client_id, redirect_uriを指定。client_secretは空白)
  • サービスEはSDKの関数からもらったAccess Tokenを利用

課題

  • カスタムURIスキームの信頼性 : 同じ値を利用するアプリがいたらどうなるか、などの解はまだ持っていない
  • Client Authenticationの強度低下 : client_idのみでAccess Tokenを取得できるという意味ではImplicitと同レベル。公式アプリを母艦にするタイプならAndroidのパッケージ署名などで少し制限できる

Client Authenticationの認証強度

  • バックエンドのWebサーバーが存在しない場合はclient_idのみの認証となる
  • なりすましをふせぐためにServer側ができることは、利用できるredirect_uriを制限することぐらい

Secret漏えいのリスク

HTTPS通信の監視やリバースエンジニアリングでclient_secretを抜かれた場合、そのclient_secretを用いて何ができるかがリスクとなる。

  • 盗んだClientになりすまし悪さして信用を奪う? → redirect_uriの制限が緩かったりすれば可能かも
  • Client Credentials Grant対応APIを利用 : 解析系のAPIやユーザー管理などを提供していた場合、利用可能になる
  • (ないとは思うが仮に)client_idとclient_secretをサービスCのバックエンドに送りアクセス元の認証を行い、データを引っ張ったりしていたらそこもやられる。

ここまでを踏まえたClient Credentialsの発行/管理方法

Client Credentials Grantを利用するAPIがある場合は以下のようにすべきと考える。

  • Public Clientにはclient_secretを発行せず、Client Credentials Grant対応APIにはアクセスさせない(Token EndpointでAccess Tokenを発行しない)
  • Server側で厳密に管理というか強制するのは難しい
  • Googleのように一つの"Project"配下にPublic, PrivateなClientをいくつかぶら下げ、PrivateなClientのみにClient Credentials Grant対応APIを提供する形式にする

Client Credentials Grantを利用するAPIがない場合はclient_secretが漏洩しても直ちに命に影響は無い。

まとめ

Client側はどう実装していくべきか

  • JSアプリから使いたいときはImplicit Grant
  • モバイル/ネイティブアプリでOAuth結果を認証に使わない場合はImplicit Grantもしくはclient_secretなしのAuthorization Code Grant
  • モバイル/ネイティブアプリでOAuth結果を認証に使う場合はclient_secretありのAuthorization Code GrantでバックエンドのWebサーバーにいろいろやらせればいいじゃない

Server側はどう実装していくべきか

  • モバイル/ネイティブアプリに使わせたいならclient_secretなしのAuthorization Code Grantをサポートするだけで良い
  • JSアプリからも使わせたいならImplicit Grantをサポート
Clone this wiki locally