OAuth2の仕様書をあらためてよんでみた。
さて先日OAuth2のredirect_uriについて書いたのだけど、いくつか気になることがあったので仕様を調べてみた。
ネイティブアプリのもつクライアントシークレットは危険なのか?
これは仕様書をみると書いてあった。
ネイティブアプリはパブリッククライアントに分類されていて、client_secretとかは抽出される可能性があるとされている。
なので正規のアプリかどうかについてclient_idとclient_secretで判別するだけでは十分ではないかもしれない。
認可サーバーと安全に認証する能力 (クライアントクレデンシャルの機密性を維持できる能力など) に基づいて, OAuthは二つのクライアントタイプを定義している.
コンフィデンシャル (confidential)
クレデンシャルの機密性を維持することができるクライアント (例えば, クライアントクレデンシャルへのアクセスが制限されたセキュアサーバー上に実装されたクライアント), または他の手段を使用したセキュアなクライアント認証ができるクライアント.
パブリック (public)
(例えば, インストールされたネイティブアプリケーションやブラウザベースのWebアプリケーションなど, リソースオーナーのデバイス上で実行されるクライアントのように) クライアントクレデンシャルの機密性を維持することができず, かつ他の手段を使用したセキュアなクライアント認証もできないクライアント.
(中略)
ネイティブアプリケーション (native application)
ネイティブアプリケーションは, リソースオーナーのデバイス上にインストールされ実行されるパブリッククライアントである. リソースオーナーはプロトコルのデータとクレデンシャルにアクセス可能である. アプリケーションに含まれるいかなるクライアント認証用のクレデンシャルも, 抽出可能であると想定される. 一方, アクセストークンやリフレッシュトークンといった動的に発行されたクレデンシャルは許容できるレベルの保護が得られる. 少なくとも, これらのクレデンシャルはアプリケーションと対話できる悪意のあるサーバーから保護されている. プラットフォームによっては, これらのクレデンシャルは同じデバイス上に存在する他のアプリケーションからも保護されているであろう.
The OAuth 2.0 Authorization Framework
ネイティブアプリケーションの認可応答用redirect_uriについてどうなってるの?
前回の記事では、redirect_uriについて占有できるURLを指定するべきと書いたけど、詳しく書いてるのはないの?とおもってしらべてみた。
認可エンドポイントはクライアントとリソースオーナーのユーザーエージェントによるインタラクションを要求する. ネイティブアプリケーションは外部のユーザーエージェントを起動もしくはアプリケーション内にユーザーエージェントを埋め込むことができる.
外部のユーザーエージェント - ネイティブアプリケーションは, クライアントを起動するためのハンドラとしてオペレーションシステムに登録したスキームをリダイレクトURIにして利用する, クレデンシャルをコピーアンドペーストする, ローカルのWebサーバーを利用する, ユーザーエクステンションをインストールする, クライアントの管理下にあるサーバー上のリソースをリダイレクトURIとして利用するなど, ネイティブアプリケーションがレスポンスにアクセスすることを可能にする手段を用いて, 認可サーバーからのレスポンスを取得することができる.
埋め込まれたユーザーエージェント - ネイティブアプリケーションは, リソースを読み込んでいる間に発生した状態変更の監視やユーザーエージェントのクッキーストレージへのアクセスなど, 埋め込まれたユーザーエージェントとの直接通信によりレスポンスを受け取ることができる.
結構いろいろ書いてあって、OAuth2の仕様ではコピペなんかも含まれていることがわかる。
おそらくAndroidで複数のアプリ候補が表示されることについては全然、許容範疇なんではないだろうか?
おそらく意図しないアプリにユーザーの手を介さず、渡ってしまうこと以外は許容範囲のように読める。カスタムURIスキームが重複した場合に後先で優先がきまってしまう場合のみに対処できればOAuth2としては十分なのではないだろうか?とおもえる
正規のクライアントかどうかの検証について、クライアントシークレットが漏れた場合の対処
実は誤解していた部分があってclient_secretは必須ではない。
正規のクライアントを確認するためにクレデンシャルが必要となっていて、仕様としてはclient_id, client_secretのペアには限定していない。ただデファクトスタンダード的にclient_secretがつかわれているらしい。
アクセストークンやリフレッシュトークンが、クライアントクレデンシャルと一緒に漏えいした場合についてはクライアントクレデンシャルを変更しろrefresh_tokenを更新できなくするほうがよいというような割りきりになっている。
クライアントの無効化あるいはクライアントクレデンシャルの変更によって, 盗まれたリフレッシュトークンの濫用を防止するなど, なんらかの攻撃にさらされたクライアントによる被害を抑える. 一連のすべてのリフレッシュトークンを無効化するよりも, クライアントクレデンシャルの変更の方が遥かにすばやく実施できる.
http://openid-foundation-japan.github.io/rfc6749.ja.html#token-endpoint-auth
下記の部分はわかりにくくて理解に苦しむ。
クライアント認証の目的でネイティブアプリケーションにクレデンシャル発行してはならないという
ことはclient_id,client_secretではクライアントを認証しないということになる。Implicit フローのみに限定しろってことだろうか?
特定デバイス上に特定の方法ならば発行してもよいというのはGoogle PlayとかApp Storeでのインストールということになるのかな。こちらには発行できるということっぽい。
これはパッケージ署名なんかの別の方法での検証を行う場合にはclient_secret使えばよいみたいに見える。
でredirect_uriの話になってここで検証しろ、他のクライアントに渡るのを回避できるとなっている様子。
認可サーバーはクライアント認証の目的でネイティブアプリケーションやユーザーエージェントベースのアプリケーションにパスワードやその他のクレデンシャルを発行してはいけない (MUST NOT).
認可サーバーは特定デバイス上に特定の方法でインストールされたネイティブアプリケーションに対しては, クライアントパスワード, もしくはその他のクレデンシャルを発行してもよい (MAY).
クライアント認証が不可能な時には, 認可サーバーはクライアントのアイデンティティ検証のために他の手段を採用すべきである (SHOULD). 例えば, クライアントのリダイレクトURIの登録要求やリソースオーナーによるアイデンティティ確認が考えられる. 有効なリダイレクトURIは, それ単体でリソース所有者の認可時にクライアントのアイデンティティを証明するには至らないが, リソースオーナーの認可取得後に偽のクライアントにクレデンシャルを配布するのを回避するのに利用することができる.
認可サーバーは認証されていないクライアントとの通信によるセキュリティ上の影響を考慮しなけれならない. そして, そのようなクライアントに対するリフレッシュトークンなどの他のクレデンシャルの開示を制限するための措置をとらなければならない.
まとめはないが
redirect_uriにhttp://localhostを用いる場合に関して、任意のポートを指定することで、認可要求を行ったクライアントに認可応答を返せるという部分についてはよいだろうが、client_secretでクライアントを認証してはいけないのであればredirect_uriにはhttp://localhost:${any-port}/callbackなどを指定できたほうがよいのだとおもう。${any-port}で数字を指定できるかどうかは知らない。http://localhost:\d+/callbackとかはかけなさそうだなとおもう。