Capacitor privacy screen を使うと、ユーザーがアプリスイッチャーを開いたときにアプリのコンテンツを隠し、Androidではスクリーンショットや画面録画もブロックできます。これがないと、iOSとAndroidは最後に表示していた画面のスナップショットを保存するため、銀行残高や医療記録、ワンタイム code が最近使用したアプリ一覧に表示されたままになります。 @capgo/capacitor-privacy-screen 1回の呼び出しまたは設定フラグ1つで追加でき、必要な画面だけを保護できます。
アプリスイッチャーに表示される情報
ユーザーがアプリを離れると、両方のシステムが現在の画面の画像をキャプチャします:
- iOSと アプリがアクティブを失うときにスナップショットを撮り、アプリスイッチャーやアプリの再起動中に一時的に表示します。
- Androidでは 「最近使ったアプリ」にライブまたはキャッシュされたプレビューを表示します。
スマホをちらりと見た人はそのスナップショットを見ることができます。画面共有している通話相手や、サポートチケット用に画面を録画しているユーザーも同様です。スクリーンショットはクラウドの写真ライブラリにも同期されるため、機密データが漏洩する主な原因となります。
導入が必要なアプリ
| アプリの種類 | 漏洩する可能性のある情報 |
|---|---|
| 銀行、フィンテック、暗号資産 | 残高、カード番号、IBAN、取引 |
| パスワードマネージャー、認証アプリ | パスワード、TOTPコード、リカバリーキー |
| ヘルスケア、医療 | 診断、処方箋、検査結果 |
| メッセージング | プライベートな会話 |
| 人事、給与、法務 | 給与、契約情報 |
| デート、プライベートメディア | 写真、マッチ |
これらのカテゴリでは、セキュリティレビューやペネトレーションテストで、アプリスイッチャー保護の欠如が日常的に指摘されます。OWASP MASVSにはUI経由で露出する機密データのチェックが含まれており、監査担当者はまさにこの点をテストします。
インストール
bun add @capgo/capacitor-privacy-screen
bunx cap sync
このプラグインはバージョン 8.x で Capacitor 8 をサポートしています。
オプション1:起動時からアプリ全体を保護する
以下で設定: capacitor.config.ts。ネイティブプラグインは読み込み時に、JavaScriptが実行される前にこれを読み取るため、最初のスナップショットさえも保護されます:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.bank',
appName: 'Example Bank',
webDir: 'dist',
plugins: {
PrivacyScreen: {
enabled: true,
android: {
dimBackground: false,
privacyModeOnActivityHidden: 'splash',
},
ios: {
blurEffect: 'dark',
},
},
},
};
export default config;
実行 bunx cap sync 設定変更後。この enabled オプションは Capacitor の設定からのみ読み取られ、ランタイムオプションではありません。
オプション2: 特定の画面を保護する
import { PrivacyScreen } from '@capgo/capacitor-privacy-screen';
export async function enterSensitiveScreen() {
await PrivacyScreen.enable({
android: { dimBackground: true },
ios: { blurEffect: 'light' },
});
}
export async function leaveSensitiveScreen() {
await PrivacyScreen.disable();
}
const { enabled } = await PrivacyScreen.isEnabled();
これらをルーターにフックし、すべてのパスの出入りをカバーします。例えば Vue Router では:
router.afterEach(async (to) => {
if (to.meta.sensitive) {
await PrivacyScreen.enable();
} else {
await PrivacyScreen.disable();
}
});
Angular では、ガードまたは NavigationEnd サブスクリプションを使用します。React Router では、レイアウトコンポーネント内のフックが有効です。重要なのは、バックナビゲーション、ディープリンク、タブ切り替えが同じロジックを通ることです。そうでないと、機密画面が保護されないままになる可能性があります。
各オプションの機能
Androidでは
- 保護機能はFLAG_SECUREを使用します
WindowManager.LayoutParams.FLAG_SECURE。有効な間は、スクリーンショットと画面録画がブロックされ、Recentsのプレビューも非表示になります。 dimBackground: trueアプリスイッチャーに暗いオーバーレイを表示します。falseでは、プラグインは利用可能な場合はスプラッシュ用のdrawableを表示し、利用できない場合は暗くする処理にフォールバックします。privacyModeOnActivityHiddenアクティビティが他の要素(たとえばシステムの生体認証プロンプト)によって隠れたときに、何を表示するかを制御します:'none'(デフォルト)、'dim'または'splash'.- で設定してください。従来の
preventScreenshotsオプションは非推奨となり、無視されます。FLAG_SECURE保護が有効な間は常に適用されます。1つの画面でスクリーンショットを許可するには、disable()をその前に呼び出し、enable()後。
iOSと
- アプリが非アクティブになると、プラグインがオーバーレイを追加し、アプリ切り替えのスナップショットにコンテンツが表示されないようにします。
blurEffect: 'light'または'dark'ネイティブのぼかしを使用します。'none'(デフォルト)利用可能な場合は起動画面を表示し、それ以外の場合はシステム背景を表示します。- 有効化中は、アプリのコンテンツもセキュアなレンダリングレイヤーに配置されます。iOS のスクリーンショットブロックはベストエフォートとして扱ってください。Apple はスクリーンショットをブロックするための公式な API を提供しておらず、動作は iOS バージョンによって変わる可能性があります。
Web
Web実装では有効フラグのみを追跡するため、codeはどこでも動作します。ブラウザはタブのプレビューを隠したり、スクリーンショットをブロックしたりすることはできません。
生体認証と組み合わせる
銀行アプリでよく見られるパターンです。アプリスイッチャーでぼかし、一定時間後にアプリが復帰した際にFace IDや指紋認証を要求します。プラグインの privacyModeOnActivityHidden は、Androidの生体認証プロンプトが表示されている間もコンテンツを覆い続けます。
import { App } from '@capacitor/app';
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
// null until the app has really gone to the background
let backgroundedAt: number | null = null;
App.addListener('appStateChange', async ({ isActive }) => {
if (!isActive) {
backgroundedAt = Date.now();
return;
}
const away = backgroundedAt === null ? 0 : Date.now() - backgroundedAt;
backgroundedAt = null;
if (away > 60_000) {
await NativeBiometric.verifyIdentity({ reason: 'Confirm it is you' });
}
});
詳細については、 ネイティブ生体認証プラグイン でセットアップとエラー処理を行います。
公式プラグインとCapgoプラグイン
Capacitorチームはまた、 @capacitor/privacy-screen (非常に類似したAPI(enable, disable, isEnabled および同じプラットフォームオプション)を公開しています。CapgoプラグインはそのAPIを維持しつつ、以下を追加します:
enabled: trueCapacitor の設定で行うため、JavaScript が実行される前から保護が有効になります。公式プラグインは JavaScript からしか有効化できません。- アプリスイッチャーのオーバーレイに加え、有効時は iOS でコンテンツのセキュアレンダリングが行われます(プラグインの動作としてドキュメント化されています)。
getPluginVersion()診断用。- 有効フラグを保持する Web 実装により、ブラウザプレビューでも同じ code が動作します。
すでに公式プラグインを使っている場合、切り替えはほぼ import の変更だけで済みます。
押さえておきたい UX の詳細
- 理由なくすべてのスクリーンショットをブロックしないでください。 ユーザーはレシートや確認番号、設定のスクリーンショットを撮ります。セキュリティポリシーで義務付けられていない限り、アプリ全体ではなく機密データを含む画面のみを保護しましょう。
- ユーザーに理由を伝えましょう。 Android でスクリーンショットを撮ろうとして画面が真っ黒になった場合、ヘルプセンターに短い説明を記載しておくだけで、サポートへの問い合わせを減らせます。
- 画面共有をテストしましょう。 Android では、~の間はビデオ通話やキャストがブロックされます。
FLAG_SECUREが設定されています。アプリに「サポートと画面共有」フローがある場合は、そのフローでは保護を無効にしてください。 - アクセシビリティを確認します。 スクリーンリーダーは
FLAG_SECUREでも引き続き動作しますが、TalkBack と VoiceOver でも必ずテストしてください。
テスト用チェックリスト
- 機密性の高い画面を開き、アプリスイッチャーに移動します。両プラットフォームでコンテンツが非表示になっている必要があります。
- Android では、スクリーンショットと画面録画を試してください。どちらも黒表示またはブロックされている必要があります。
- 生体認証プロンプトをトリガーし、背景を確認します。
- 機密性の高い画面から通常画面に戻ります。画面単位モードを使用している場合、保護はオフになるはずです。
- アプリを終了して再起動します。
enabled: trueが設定されている場合、最初のスナップショットは保護されている必要があります。 - Split View と Slide Over を使用している iPad でテストします。非アクティブな状態でもアプリが表示され続ける可能性があります。
トラブルシューティング
iOSでスナップショットにコンテンツがまだ表示される。 アプリがすでにバックグラウンドに移行してから保護を有効にした、または一部のルートでしか有効にしていない場合です。アプリ全体を保護するには設定オプションを使用してください。
Androidの最近使ったアプリで、スプラッシュではなく黒画面が表示される。 設定 dimBackground: false また、スプラッシュ用のdrawableが存在することを確認してください。存在しない場合、プラグインはディミングにフォールバックします。
Androidではスクリーンショットがまだ撮れてしまいます。 確認 isEnabled()。アプリの別の部分が disable() をナビゲーション時に呼び出している可能性があります。
OTA更新後に保護が失われる。 設定値はネイティブです。変更する場合 plugins.PrivacyScreen における capacitor.config.ts、ストアに新しいビルドをリリースします。ランタイム enable() と disable() のJavaScript呼び出しは、 Capgo Live Updates.
関連するセキュリティ対策
プライバシー画面はモバイルセキュリティチェックリストの1項目にすぎません。セキュアストレージ、証明書ピンニング、ルート検出も確認してください。Capgo には SSLピニング と ルートおよびジェイルブレイク検出、および の Capacitor アプリに関するAppleのプライバシールールについての投稿 レビュー側をカバーしています。プラグインの全オプションは Privacy Screen のドキュメント.