この情報は、インテント リダイレクトに対する脆弱性があるアプリのデベロッパーを対象としています。
現在の状況
1 つ以上のアプリに、インテント リダイレクトの問題があります。この問題により、非公開のアプリ コンポーネントやファイルが悪意のあるアプリから不正にアクセスされる恐れがあります。下記の詳細な手順を確認して、アプリの問題を解決してください。Play Console に表示されている期限を過ぎても、セキュリティの脆弱性が修正されないアプリはすべて、Google Play から削除されます。
必要な対応
- Play Console にログインし、[アラート] セクションで該当するアプリや問題の解決期限を確認します。
- 下記の手順に沿って該当するアプリを更新します。
- 該当するアプリの更新バージョンを送信します。
再送信すると、アプリは再度審査されます。審査には数時間ほどかかることがあります。アプリが審査に合格して正常に公開された場合は、これ以上の対応は不要です。アプリの審査が不合格となった場合は、新しいバージョンのアプリは公開されず、通知メールが届きます。
その他の詳細
信頼できないインテントを使用して、コンポーネントを起動したり(startActivity を呼び出すなど)、データを返したり(setResult の呼び出しによってなど)することは危険です。これによって、悪意のあるアプリが次のような問題を引き起こすおそれがあります。
- 機密性の高いファイルやシステムデータ(SMS メッセージなど)をアプリから盗む
- 引数を悪用してアプリの非公開コンポーネントを起動する
信頼できないインテントにおいて、インテントの検証やサニタイズをすることなく startActivity、startService、sendBroadcast、または setResult をアプリが呼び出さないようにすることが重要です。
以下のいずれかの方法でこの脆弱性への対策を講じてください。
方法 1: 抽出されるインテントのリダイレクト元である、該当するアプリ コンポーネントを非公開にする。
該当するアプリ コンポーネントが、他のアプリからインテントを受け取る必要がない場合は、マニフェストに android:exported=”false” を設定して、そのアプリ コンポーネントを非公開にすることができます。
方法 2: 抽出されるインテントの提供元が信頼できるかどうか確認する。
提供元のアクティビティが信頼できるかどうかを確認するには、getCallingActivity などのメソッドを使用します。次に例を示します。
// 提供元のアクティビティのパッケージが信頼できるかどうか確認
if (getCallingActivity().getPackageName().equals(“known”)) {
Intent intent = getIntent();
// ネストされているインテントを抽出
Intent forward = (Intent) intent.getParcelableExtra(“key”);
// ネストされているインテントをリダイレクト
startActivity(forward);
}
注:
- getCallingActivity() が null 以外の値を返すかどうかを確認するだけでは、脆弱性を回避するのに不十分です。悪意のあるアプリがこの関数に null 値を提供することがあります。
- Google Play 開発者サービスの SMS Retriever Auth の場合、SEND_PERMISSION を使用してブロードキャスト レシーバを保護することで、インテントが必ず Play 開発者サービスから提供されるようにします。
方法 3: リダイレクト対象のインテントが有害ではないことを確認する。
リダイレクトするインテントの動作を確認します。
- 送信先がアプリの非公開コンポーネントではないこと、かつ
- 送信先が外部アプリのコンポーネントでもないこと。設計上リダイレクト先を外部アプリにする場合は、そのインテントがアプリの非公開コンテンツ プロバイダまたはシステム データへの URI 権限を付与していないことを確認します。
インテントをリダイレクトする前に、インテントの処理にどのコンポーネントが使用されるかをアプリが確認するには、resolveActivity などのメソッドを使用します。次に例を示します。
Intent intent = getIntent();
// ネストされたインテントを抽出
Intent forward = (Intent) intent.getParcelableExtra(“key”);
// コンポーネント名を取得
ComponentName name = forward.resolveActivity(getPackageManager());
// パッケージ名とクラス名が意図したものかどうか確認
if (name.getPackageName().equals(“safe_package”) &&
name.getClassName().equals(“safe_class”)) {
// ネストされたインテントをリダイレクト
startActivity(forward);
}
インテントが URI 権限を付与するかどうかをアプリが確認するには、getFlags などのメソッドを使用します。次に例を示します。
// ネストされたインテントを抽出
Intent forward = (Intent) intent.getParcelableExtra(“key”);
// フラグを取得
int flags = forward.getFlags();
// ネストされたインテントが URI 権限を付与しないかどうか確認
if ((flags & Intent.FLAG_GRANT_READ_URI_PERMISSION == 0) &&
(flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION == 0)) {
// ネストされたインテントをリダイレクト
startActivity(forward);
}
removeFlags を使用して、アプリが URI 権限の付与を取り消すこともできます。次に例を示します。
// 信頼できないインテント
Intent intent = getIntent();
// 信頼できないインテントの URI 権限の付与を取り消す
intent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// 信頼できないインテントを渡す
setResult(intent);
アラートの把握と回避方法の選択
Google Play Console のアラートは、信頼できないインテントを使って startActivity、startActivityForResult、startService、sendBroadcast、または setResult をアプリがどこで呼び出しているかを報告します。どの回避方法が最適かをよく理解するためには、メソッドの呼び出し元をトレースバックし、信頼できないインテントの発生元を探します。たとえば、方法 1 の場合、トレースバックして非公開にするコンポーネントを判断します。
サポートのご案内
この脆弱性に関する技術的なご質問は、Stack Overflow に投稿してください。その際には「android-security」タグをご使用ください。この問題を解決するにあたって手順にご不明な点がある場合は、デベロッパー サポートチームにお問い合わせください。