62

Android の AccountManager は、異なる UID を持つアプリに対してキャッシュされた同じ認証トークンをフェッチするように見えます - これは安全ですか? アクセス トークンは異なるクライアント間で共有されることが想定されていないため、OAuth2 との互換性はないようです。

背景/コンテキスト

OAuth2プロバイダーであるサーバーへのREST APIリクエストの認証/承認にOAuth2を使用するAndroidアプリを構築しています。このアプリは (サードパーティのアプリではなく) 「公式」アプリであるため、信頼できる OAuth2 クライアントと見なされます。そのため、OAuth2 トークンを取得するためにリソース所有者のパスワード フローを使用しています: ユーザー (リソース所有者)ユーザー名/パスワードをアプリに入力すると、クライアント ID とクライアント シークレットがユーザー資格情報と共にサーバーの OAuth2 トークン エンドポイントに送信され、API 呼び出しに使用できるアクセス トークンと long-有効期限が切れたときに新しいアクセス トークンを取得するために使用されるライブ リフレッシュ トークン。これは、ユーザーのパスワードよりも更新トークンをデバイスに保存する方が安全だからです。

デバイスでアカウントと関連するアクセス トークンを管理するためにAccountManagerを利用しています。私は独自の OAuth2 プロバイダーを提供しているので、この Android 開発ガイドで説明され、SampleSyncAdapter サンプル プロジェクトで示されているように、 AbstractAccountAuthenticatorとその他の必要なコンポーネントを拡張して、独自のカスタム アカウント タイプを作成しました。アプリ内からカスタム タイプのアカウントを正常に追加し、[アカウントと同期] Android 設定画面からそれらを管理できます。

問題

ただし、AccountManager が認証トークンをキャッシュして発行する方法に関心があります。具体的には、特定のアカウント タイプとトークン タイプの同じ認証トークンが、ユーザーがアクセスを許可したすべてのアプリからアクセスできるように見えることです。

AccountManager を介して認証トークンを取得するには、 AccountManager.getAuthToken() を呼び出して、認証トークンを取得するAccountインスタンスと目的のauthTokenType. 指定されたアカウントと authTokenType の認証トークンが存在し、ユーザーが認証トークン要求を行ったアプリにアクセスを許可した場合 ( 「アクセス要求の許可」画面を介して) (要求元のアプリの UID が一致しない場合など)オーセンティケーターの UID)、トークンが返されます。私の説明が不足している場合は、この役立つブログ エントリで非常に明確に説明されています。その投稿に基づいて、AccountManagerAccountManagerServiceのソースを調べた後(AccountManager の面倒な作業を行う内部クラス) 私自身の場合、authTokenType/account コンボごとに 1 つの認証トークンのみが保存されているようです。

したがって、悪意のあるアプリが認証システムで使用されているアカウントの種類と authTokenType を知っていた場合、ユーザーが悪意のあるアプリにアクセスを許可したと仮定して、AccountManager.getAuthToken() を呼び出して、アプリに保存されている OAuth2 トークンへのアクセスを取得できる可能性があります。アプリ。

私にとっての問題は、AccountManager のデフォルトのキャッシュ実装がパラダイムに基づいて構築されていることです。OAuth2 認証/承認コンテキストをレイヤー化すると、電話/デバイスがサービス/リソース プロバイダーの単一の OAuth2 クライアントであると見なされます。 . 一方、私にとって理にかなっているパラダイムは、各アプリ/UID を独自の OAuth2 クライアントと見なす必要があるということです。OAuth2 プロバイダーがアクセス トークンを発行するとき、デバイス上のすべてのアプリではなく、正しいクライアント ID とクライアント シークレットを送信した特定のアプリのアクセス トークンを発行しています。たとえば、ユーザーは私の公式アプリ (アプリ クライアント A と呼びます) と、私の API を使用する "ライセンスされた" サードパーティ アプリ (アプリ クライアント B と呼びます) の両方をインストールしている可能性があります。公式のクライアント A の場合、私の OAuth2 プロバイダーは、API のパブリック部分とプライベート部分の両方へのアクセスを許可する「スーパー」タイプ/スコープ トークンを発行する場合がありますが、サードパーティのクライアント B の場合、私のプロバイダーは「制限付き」タイプを発行する場合があります。パブリック API 呼び出しへのアクセスのみを許可する /scope トークン。アプリ クライアント B がアプリ クライアント A のアクセス トークンを取得することはできません。ユーザーがクライアント A のスーパー トークンに対してクライアント B に承認を付与したとしても、私の OAuth2 プロバイダーはそのトークンをクライアント A に付与することのみを意図していたという事実は残ります。

ここで何かを見落としていますか?アプリごと/UIDベース(各アプリは個別のクライアント)で認証トークンを発行する必要があるという私の考えは合理的/実用的ですか、それともデバイスごとの認証トークン(各デバイスはクライアントです)が標準/承認されていますか練習?

または、 / に関するコード/セキュリティ制限の理解に何らかの欠陥があるため、AccountManagerこのAccountManagerService脆弱性は実際には存在しませんか? 上記のクライアント A/クライアント B のシナリオをAccountManagerとカスタム オーセンティケーターでテストしたところ、異なるパッケージ スコープと UID を持つテスト クライアント アプリ B は、サーバーがテスト用に発行した認証トークンを取得できました。クライアントアプリAを同じものに渡しますauthTokenType(その間、「アクセスリクエスト」許可画面でプロンプトが表示されましたが、私はユーザーなので無知なので承認しました)...

可能な解決策

を。"Secret" authTokenType
認証トークンを取得するには、 を知っているauthTokenType必要があります。authTokenType特定のシークレットトークンタイプに対して発行されたトークンが、シークレットトークンタイプを知っている「承認された」クライアントアプリのみによって取得されるように、クライアントシークレットのタイプとして扱われるべきですか? これはあまり安全ではないようです。ルート化されたデバイスでは、システムのテーブルのauth_token_type列を調べることができますauthtokensaccountsデータベースにアクセスし、トークンと共に保存されている authTokenType 値を調べます。したがって、私のアプリ (およびデバイスで使用される承認済みのサードパーティ アプリ) のすべてのインストールで使用される「秘密の」認証トークン タイプは、1 つの中央の場所で公開されます。少なくとも OAuth2 クライアント ID/シークレットを使用すると、アプリと一緒にパッケージ化する必要がある場合でも、それらはさまざまなクライアント アプリに分散されており、それらを難読化する試みが行われる可能性があります (何もないよりはましです)。アプリをアンパッケージ/逆コンパイルします。

b. カスタム認証トークンAccountManager.KEY_CALLER_UIDAuthenticatorDescription.customTokens
のドキュメント、および以前に参照したソース コードによると、カスタム アカウント タイプが「カスタム トークン」を使用することを指定し、独自のトークン キャッシング/ストレージの実装を内部でスピンできるはずです。私のカスタムオーセンティケーター。UIDごとに認証トークンを保存/取得するために、呼び出し元アプリのUIDを取得できます。基本的に、追加されることを除いて、デフォルトの実装のようなテーブルがありますAccountManagerServiceauthtokensuidcolumn so that tokens are uniquely indexed on U̲I̲D̲, a̲c̲c̲o̲u̲n̲t̲, and A̲u̲t̲h̲ ̲T̲o̲k̲e̲n̲ ̲T̲y̲p̲e̲ (as opposed to just a̲c̲c̲o̲u̲n̲t̲ and A̲u̲t̲h̲ ̲T̲o̲k̲e̲n̲ ̲T̲y̲p̲e̲). これは、「秘密の」authTokenTypes を使用するよりも安全なソリューションのようです。これはauthTokenTypes、アプリ/認証システムのすべてのインストールで同じものを使用する必要があるためです。一方、UID はシステムごとに異なり、簡単にスプーフィングすることはできません。独自のトークン キャッシング メカニズムを作成して管理するための嬉しいオーバーヘッドは別として、セキュリティの観点から、このアプローチにはどのような欠点がありますか? やり過ぎですか?私は本当に何かを保護しているのでしょうか、それとも、そのような実装が整っていても、ある悪意のあるアプリ クライアントが別のアプリ クライアントを簡単に取得できるような何かが欠けているのでしょうか。AccountManagerauthTokenType(s) 秘密であることが保証されていない (前述の悪意のあるアプリが OAuth2 クライアント シークレットを知らないため、新しいトークンを直接取得することはできずAccountManager、承認されたユーザーに代わって既にキャッシュされているトークンを取得することしか期待できないと仮定します)アプリクライアント)?

c. OAuth2 トークン
を使用してクライアント ID/シークレットを送信するAccountManagerServiceのデフォルトのトークン ストレージ実装を使用し、アプリの認証トークンへの不正アクセスの可能性を受け入れることもできますが、API リクエストに OAuth2 クライアント ID とクライアント シークレットを常に含めるように強制することもできます。アクセストークンに加えて、アプリが最初にトークンが発行された承認されたクライアントであることをサーバー側で確認します。ただし、これは避けたいと思います。なぜなら、A)私の知る限り、OAuth2 仕様では、保護されたリソース要求に対してクライアント認証は必要ありません。アクセス トークンのみが必要であり、B)毎回クライアントを認証する追加のオーバーヘッドを回避したいからです。リクエスト。

これは一般的なケースでは不可能です (サーバーが取得するのは、プロトコル内の一連のメッセージだけです。これらのメッセージを生成したコードを特定することはできません)。--マイケル

しかし、クライアントが最初にアクセス トークンを発行する OAuth2 フローの最初のクライアント認証についても同じことが言えます。唯一の違いは、トークン要求だけで認証するのではなく、保護されたリソースの要求も同じ方法で認証されることです。(クライアント アプリは、その c̲l̲i̲e̲n̲t̲ ̲i̲d̲ と c̲l̲i̲e̲n̲t̲ ̲s̲e̲c̲r̲e̲t̲ のloginOptionsパラメーターを介して渡すことができることに注意してくださいAccountManager.getAuthToken()。これは、OAuth2 プロトコルに従って、私のカスタム オーセンティケーターがリソース プロバイダーに渡すだけです)。


主な質問

  1. 同じ authTokenType で AccountManager.getAuthToken() を呼び出すことにより、あるアプリが別のアプリのアカウントの authToken を取得することは本当に可能ですか?
  2. これが可能である場合、これはOAuth2 コンテキスト内で有効/実用的なセキュリティ上の問題ですか?

    ユーザーに与えられた認証トークンがそのユーザーからの秘密のままであることに決して依存することはできません...そのため、Android がその設計のあいまいさの目標によってこのセキュリティを無視することは合理的です-Michael

    BUT - ユーザー (リソース所有者) が私の同意なしに認証トークンを取得することについては心配していません。不正なクライアントが心配です(アプリ)。ユーザーが自分の保護されたリソースの攻撃者になりたい場合は、自分自身をノックアウトできます。私は、ユーザーが私のクライアント アプリをインストールし、無意識のうちに、正しい authTokenType を渡したという理由だけでアプリの認証トークンにアクセスできる「なりすまし」クライアント アプリをインストールすることは可能であってはならないと言っています。アクセス要求画面を調べるのが怠惰/無知/急いでいる。この類推は少し単純化しすぎているかもしれませんが、インストールされている Facebook アプリが Gmail アプリによってキャッシュされたメールを読み取れないことは「あいまいさによるセキュリティ」とは考​​えていません。これは、私 (ユーザー) が電話をルート化してキャッシュを調べることとは異なります。内容は自分。

    ユーザーは、アプリがあなたのトークンを使用するための (Android システムが提供する) アクセス要求を受け入れる必要がありました...それを考えると、Android ソリューションは問題ないようです - アプリは、尋ねることなくユーザーの認証を黙って使用することはできません --マイケル

    しかし、これは承認の問題でもあります。「公式」クライアントに対して発行された認証トークンは、そのクライアントとそのクライアントのみが承認される保護されたリソースのセットへの鍵です。ユーザーはこれらの保護されたリソースの所有者であるため、サードパーティのクライアント (「承認された」パートナー アプリまたはフィッシャー) からのアクセス要求を受け入れる場合、事実上、サードパーティを承認していると主張できると思います。それらのリソースへのアクセスを要求したパーティ クライアント。しかし、私はこれに問題があります:

    • 平均的なユーザーは、この決定を適切に行うことができるほどセキュリティを意識していません。Android のアクセス要求画面で「拒否」をタップするのは、ユーザーの判断だけに頼って、下手なフィッシング攻撃でも防げるとは思いません。ユーザーにアクセス要求が提示されると、私のオーセンティケーターは非常に詳細になり、ユーザーが要求を受け入れた場合に許可する機密保護されたリソースのすべてのタイプ (クライアントのみがアクセスできるようにする必要があります) を列挙することができます。そしてほとんどの場合、ユーザーはまだあまりにも無知であり、受け入れようとしています. また、他のより巧妙なフィッシングの試みでは、「なりすまし」アプリは、ユーザーがアクセス要求画面で眉をひそめるにはあまりにも「公式」に見えるだけです。か、ここ」「このリクエストを受け入れないでください。この画面が表示されている場合は、悪意のあるアプリがあなたのアカウントにアクセスしようとしています!」このような場合、ほとんどのユーザーがリクエストを拒否することを願っています。しかし、なぜそこまでする必要があるのでしょうか。Android が認証トークンを発行対象の各アプリ/UID の範囲に分離したままにしておくだけであれば、これは問題になりません。単純化しましょう - 私が「公式」クライアント アプリを 1 つしか持っていない場合でも、リソース プロバイダーは他のサードパーティ クライアントにトークンを発行することさえ気にしません。 AccountManager、「いいえ。この認証トークンをロックダウンして、私のアプリだけがアクセスできるようにしてください。」「カスタム トークン」ルートを使用すればこれを行うことができますが、その場合でも、ユーザーに最初にアクセス リクエスト画面が表示されるのを防ぐことはできません。少なくとも、
    • Android のドキュメントでさえ、OAuth2を認証 (およびおそらく承認)の「業界標準」として認識しています。OAuth2 仕様では、アクセス トークンをクライアント間で共有したり、いかなる方法でも漏らしたりしてはならないと明確に述べています。では、デフォルトの AccountManager の実装/構成により、クライアントは、別のクライアントがサービスから最初に取得した同じキャッシュされた認証トークンを簡単に取得できるのはなぜでしょうか? AccountManager 内での簡単な修正は、キャッシュされたトークンをサービスから最初に取得したときと同じアプリ/UID に対してのみ再利用することです。特定の UID で使用できるローカルにキャッシュされた認証トークンがない場合は、サービスから取得する必要があります。または、少なくともこれを開発者向けの構成可能なオプションにします。
    • OAuth 3-legged フロー (ユーザーがクライアントへのアクセスを許可することを含む) では、A)クライアントを認証し、B )クライアントが有効な場合、ユーザーにアクセス許可要求を提示しますか? Android が (誤って) 流れの中でこの役割を奪っているようです。

    しかし、ユーザーはアプリが以前の認証をサービスに再利用することを明示的に許可できます。これはユーザーにとって便利です.-- Michael

    しかし、利便性の ROI がセキュリティ リスクを正当化するとは思いません。ユーザーのパスワードがユーザーのアカウントに保存されている場合、実際にユーザーが購入できる唯一の利便性は、Web リクエストをサービスに送信して、実際に承認された新しい個別のトークンを取得することです。要求しているクライアントに対して、クライアントに対して承認されていないローカルにキャッシュされたトークンが返されます。そのため、ユーザーは、リソースが盗まれたり悪用されたりしてユーザーが大きな不便を被るリスクを冒して、「サインイン...」進行状況ダイアログを数秒短く表示するというわずかな利便性を得ることができます。

  3. A) OAuth2 プロトコルを使用して API リクエストを保護すること、B)独自の OAuth2 リソース/認証プロバイダーを提供すること (たとえば、Google や Facebook で認証するのではなく)、およびC) Android の AccountManager を利用してカスタムアカウントタイプとそのトークンを管理する場合、提案されたソリューションは有効ですか? どれが最も理にかなっていますか? 長所/短所を見落としていますか? 私が考えたことのない価値のある代替手段はありますか?

    [用途] 代替クライアント公式クライアントのみがアクセスできるようにする秘密の API はありません。人々はこれを回避します。ユーザーが使用している (将来の) クライアントに関係なく、すべての公開 API が安全であることを確認してください -- Michael

    しかし、これはそもそも OAuth2 を使用する主な目的の 1 つを無効にしないでしょうか? すべての潜在的な被承認者が保護されたリソースの同じスコープに対して承認される場合、承認は何の役に立つでしょうか?

  4. これが問題だと感じた人はいますか? また、どのように回避しましたか? 他の人がこれをセキュリティの問題/懸念であると感じているかどうかを調べるために、大規模なグーグル検索を行いましたが、Android の AccountManager と認証トークンに関連するほとんどの投稿/質問は、Google アカウントで認証する方法に関するものであり、そうではないようですカスタム アカウント タイプと OAuth2 プロバイダーを使用します。さらに、同じ認証トークンが異なるアプリで使用される可能性について懸念している人を見つけることができませんでした。これは、そもそもこれが本当に可能性があるかどうか、または懸念に値するかどうか疑問に思います (私の最初の 2 つの「重要な質問」を参照してください)。 」に記載されています)。

ご意見/ご指導をよろしくお願いいたします。


に応答して...

マイケルの答え- あなたの答えで私が抱えている主な問題は次のとおりです。

  1. ユーザー/電話/デバイス自体が1つの「大きな」クライアントであるのとは対照的に、アプリをサービスの個別の個別のクライアントと考える傾向があるため、1つのアプリに対して承認されたトークンはそうすべきではありません。デフォルト、持っていないものに譲渡できます。次の可能性があるため、各アプリを個別のクライアントと見なすことは意味がないとほのめかしているようです。

    ユーザーがルート化された電話を実行している可能性があり、トークンを読み取り、プライベート API へのアクセスを取得する可能性があります... [または] ユーザーのシステムが侵害された場合 (この場合、攻撃者はトークンを読み取ることができます)

    したがって、デバイス自体のアプリ間のセキュリティを保証することはできないため、大まかに言えば、デバイスをサービスのクライアントと見なす必要があります。システム自体が侵害された場合、そのデバイスからサービスに送信されるリクエストを認証/承認する保証はありません。しかし、TLS についても同じことが言えます。エンドポイント自体を保護できない場合、トランスポート セキュリティは関係ありません。また、侵害されていない大多数の Android デバイスでは、同じ認証トークンを共有してすべてを 1 つにまとめるのではなく、各アプリ クライアントを個別のエンドポイントと見なす方が安全であると考えています。

  2. 「アクセス要求」画面が表示された場合 (同意してインストールする前に常によく読んでいるソフトウェアの使用許諾契約に似ています)、悪意のある/承認されていないクライアント アプリとそうでないものを区別するユーザーの判断を信用できません。
4

4 に答える 4

10

これは有効/実用的なセキュリティ上の懸念事項ですか?

公式のクライアント A の場合、OAuth2 プロバイダーは、API のパブリック部分とプライベート部分の両方へのアクセスを許可する「スーパー」タイプ/スコープ トークンを発行する場合があります。

一般的なケースでは、ユーザーに与えられた認証トークンがそのユーザーの秘密のままであることを信頼することはできません。たとえば、ルート化された電話を実行しているユーザーがトークンを読み取って、プライベート API にアクセスすることができます。ユーザーのシステムが侵害された場合も同様です (この場合、攻撃者はトークンを読み取ることができます)。

別の言い方をすれば、認証されたすべてのユーザーが同時にアクセスできる「プライベート」API などというものは存在しないため、Android がその設計におけるあいまいさの目標によってこのセキュリティを無視することは合理的です。

悪意のあるアプリ ... アプリに保存されている OAuth2 トークンにアクセスできる

悪意のあるアプリの場合、Android のパーミッション システムが悪意のあるアプリを分離することを期待しているため、悪意のあるアプリがクライアントのトークンを使用できないことがより合理的に聞こえ始めます (ユーザーがアクセス許可を読んだり気にしたりしている場合)。インストール時に受け入れられます)。ただし、あなたが言うように、ユーザーはアプリがトークンを使用するための (Android システムが提供する) アクセス要求を受け入れる必要がありました。

それを考えると、Android ソリューションは問題ないようです。アプリはユーザーの認証を確認せずに黙って使用することはできませんが、ユーザーは、アプリが以前の認証をサービスに再利用することを明示的に許可できます。これはユーザーにとって便利です。

可能な解決策のレビュー

"Secret" authTokenType ... あまり安全ではないようです

同意 - それはあいまいさによるセキュリティの別のレイヤーにすぎません。認証を共有したいアプリはいずれにしても、authTokenType が何であるかを調べなければならなかったように思えます。そのため、このアプローチを採用すると、この架空のアプリ開発者にとっては少し扱いに​​くくなります。

OAuth2 トークンを使用してクライアント ID/シークレットを送信する ... [to] アプリが承認されたクライアントであることをサーバー側で確認する

これは一般的なケースでは不可能です (サーバーが取得するのは、プロトコル内の一連のメッセージだけです。これらのメッセージを生成したコードを特定することはできません)。この特定の例では、(ルート以外の)代替クライアント/悪意のあるアプリのより限定的な脅威から保護する可能性があります.AccountManagerについてコメントするのに十分ではありません(カスタム認証トークンソリューションについても同様です)。

提案

ユーザーが自分のアカウントにアクセスしたくない悪意のあるアプリと、API の一部を使用したくない代替クライアントの 2 つの脅威について説明しました。

  • 悪意のあるアプリ: 提供しているサービスの機密性を考慮してください。たとえば、Google や Twitter のアカウントほど機密性が高くない場合は、Android の保護機能 (インストール時の権限、アクセス リクエスト画面) に頼ってください。より機密性が高い場合、Android の AccountManager を使用するという制約が適切かどうかを検討してください。アカウントの悪用からユーザーを強力に保護するには、危険なアクションに対して 2 要素認証を試してください (オンライン バンキングでの新しい受信者のアカウントの詳細の追加を参照)。

  • 代替クライアント: 公式クライアントのみがアクセスできるようにする秘密の API はありません。人々はこれを回避します。ユーザーが使用している (将来の) クライアントに関係なく、すべての公開 API が安全であることを確認します。

于 2013-01-22T22:00:03.813 に答える
2

あなたの観察は正しいです。Authenticator は、インストールするアプリと同じ UID で実行されます。別のアプリがアカウント マネージャーに接続し、このオーセンティケーターのトークンを取得すると、提供されたオーセンティケーター サービスにバインドされます。これは UID として実行されるため、新しいアカウントはこの Authenticator に関連付けられます。アプリが getAuthToken を呼び出すと、バインドが行われ、Authenticator は引き続き同じ UId で実行されます。デフォルトのビルトイン アクセス許可は、アカウントの UID をチェックするため、異なるオーセンティケータが別のオーセンティケータから別のアカウントにアクセスすることはできません。

この問題は、アカウント マネージャー サービスによってバンドルに追加されるため、addAccount と GetAuthToken に「呼び出し元の UID」を使用することで解決できます。オーセンティケーターの実装でそれを確認できます。

   @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
            String authTokenType, Bundle loginOptions) throws NetworkErrorException {
        Log.v(
                TAG,
                "getAuthToken() for accountType:" + authTokenType + " package:"
                        + mContext.getPackageName() + "running pid:" + Binder.getCallingPid()
                        + " running uid:" + Binder.getCallingUid() + " caller uid:"
                        + loginOptions.getInt(AccountManager.KEY_CALLER_UID));
          ...
}

他の開発者がクライアント シークレットを抽出できるため、ネイティブ アプリにクライアント シークレットを保存する代わりに、承認フローに従うことをお勧めします。あなたのアプリはウェブアプリではないので、シークレットを持つべきではありません。

アカウントを追加するときは、callingUId も照会できます。アプリの UID として実行されるaddAccount関連のアクティビティでsetUserDataを設定する必要があるため、setUserDataを呼び出すことができます。

getUserDatasetUserDataは組み込みの sqllite データベースを使用するため、自分でキャッシュを構築する必要はありません。文字列型のみを保存できますが、json を解析して、アカウントごとに追加情報を保存できます。

別のサードパーティ アプリがアカウントにクエリを実行し、アカウントで getAuthtoken を呼び出すと、アカウントのユーザーデータで UID を確認できます。呼び出し元の UID がリストされていない場合は、プロンプトやその他の操作を行って許可を得ることができます。許可されている場合は、新しい UID をアカウントに追加できます。

アプリ間でのトークンの共有: 通常、各アプリは異なる clientid で登録されており、トークンを共有するべきではありません。トークンはクライアント アプリ用です。

ストレージ: AccountManager はデータを暗号化していません。より安全なソリューションが必要な場合は、トークンを暗号化してから保存する必要があります。

于 2014-03-14T07:52:00.280 に答える
1

@Michael が質問に完璧に答えたと思います。ただし、迅速な回答を探している人にとって回答をより賢明で簡潔にするために、これを書いています。

Androidのセキュリティに関するあなたの懸念AccountManagerは正しいですが、これがOAuthの意図であり、AndroidがAccountManager依存しています。

つまり、非常に安全な認証メカニズムを探している場合、これは適切なオプションではありません。侵入者に誤ってアクセス許可を与えたり、ルート化されたデバイスを実行したりするなど、ユーザーのデバイスにセキュリティの脆弱性がある場合に、侵入者にトークンが簡単に明らかになる可能性があるため、認証のためにキャッシュされたトークンに依存しないでください。

オンライン バンキング アプリなどのより安全な認証システムで OAuth に代わる優れた方法は、公開鍵と秘密鍵を使用した非対称暗号化を使用することです。この場合、ユーザーはサービスを使用するたびにパスワードを入力する必要があります。パスワードは、デバイスの公開鍵を使用して暗号化され、サーバーに送信されます。ここで、侵入者が暗号化されたパスワードを知ったとしても、その公開鍵でパスワードを解読することはできず、サーバーの秘密鍵しか必要としないため、侵入者はそれで何もできません。

いずれにせよ、AccountManagerAndroid のシステムを利用し、高いセキュリティを維持したい場合は、デバイスにトークンを保存しないことで可能になります。からのgetAuthTokenメソッドは、次のAbstractAccountAuthenticatorようにオーバーライドできます。

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String
        authTokenType, Bundle options) throws NetworkErrorException {
    AuthenticatorManager authenticatorManager = AuthenticatorManager.authenticatorManager;
    Bundle result;
    AccountManager accountManager = AccountManager.get(context);
    // case 1: access token is available
    result = authenticatorManager.getAccessTokenFromCache(account, authTokenType,
            accountManager);
    if (result != null) {
        return result;
    }
    final String refreshToken = accountManager.getPassword(account);
    // case 2: access token is not available but refresh token is
    if (refreshToken != null) {
        result = authenticatorManager.makeResultBundle(account, refreshToken, null);
        return result;
    }
    // case 3: neither tokens is available but the account exists
    if (isAccountAvailable(account, accountManager)) {
        result = authenticatorManager.makeResultBundle(account, null, null);
        return result;
    }
    // case 4: account does not exist
    return new Bundle();
}

この方法では、ケース 1、ケース 2、ケース 4 のいずれにも該当しません。これは、保存されたトークンaccountが存在するにもかかわらず、トークンが保存されていないためです。したがって、ケース 3 のみが返され、関連するコールバックで設定してActivity、ユーザーが認証のためにユーザー名とパスワードを入力する を開くことができます。

ここでこれをさらに説明するのが正しい軌道に乗っているかどうかはわかりませんが、私のウェブサイトの投稿AccountManagerが念のため役立つかもしれません.

于 2016-12-10T11:45:07.373 に答える