Una privacy screen Capacitor nasconde i contenuti dell'app quando l'utente apre l'app switcher e, su Android, blocca screenshot e registrazione schermo. Senza di essa, iOS e Android scattano un'istantanea dell'ultima schermata, lasciando visibili saldi bancari, dati medici o un code usa e getta nella lista delle app recenti. @capgo/capacitor-privacy-screen lo aggiunge con una sola chiamata o un flag di config, e ti lascia proteggere solo le schermate che ne hanno bisogno.
Cosa espone l'app switcher
Quando un utente esce dalla tua app, entrambi i sistemi catturano un'immagine dello schermo corrente:
- iOS scatta uno snapshot quando l'app smette di essere attiva e lo mostra nell'app switcher, e brevemente durante il riavvio dell'app.
- Android mostra un'anteprima live o in cache nelle App Recenti.
Chiunque guardi il telefono vede quello snapshot. Lo stesso vale per chi condivide lo schermo durante una chiamata o per un utente che registra lo schermo per un ticket di supporto. Gli screenshot vengono anche sincronizzati con le librerie fotografiche cloud, ed è proprio lì che i dati sensibili rischiano di trapelare.
Quali app ne hanno bisogno
| Tipo di app | Cosa può trapelare |
|---|---|
| Settore bancario, fintech, crypto | Saldo, numeri carta, IBAN, transazioni |
| Password manager, authenticator | Password, codici TOTP, chiavi di recupero |
| Salute e ambito medico | Diagnosi, prescrizioni, referti |
| Messaging | Conversazioni private |
| HR, payroll, legale | Stipendi, contratti |
| App di dating, media privati | Photos and matches |
Security review e penetration test per queste categorie segnalano spesso la mancanza di protezione nell'app switcher. OWASP MASVS include controlli sui dati sensibili esposti tramite UI, e gli auditor testano proprio questo.
Installa
bun add @capgo/capacitor-privacy-screen
bunx cap sync
Il plugin supporta Capacitor 8 con la versione 8.x.
Opzione 1: proteggi l'intera app all'avvio
Impostalo in capacitor.config.ts. Il plugin nativo lo legge al caricamento, prima dell'esecuzione del JavaScript, così anche il primo snapshot è protetto:
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;
Esegui bunx cap sync dopo aver modificato la config. La enabled opzione viene letta solo dalla config di Capacitor, non è un'opzione di runtime.
Opzione 2: proteggere schermi specifici
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();
Collega questi hook al tuo router per coprire ogni percorso in entrata e in uscita. Con Vue Router, ad esempio:
router.afterEach(async (to) => {
if (to.meta.sensitive) {
await PrivacyScreen.enable();
} else {
await PrivacyScreen.disable();
}
});
Con Angular, usa una guard o una NavigationEnd subscription. Con React Router, basta un hook nel layout component. L'importante è che la navigazione indietro, i deep link e il cambio tab seguano la stessa logica, altrimenti uno schermo sensibile potrebbe restare scoperto.
Cosa fa ogni opzione
Android
- La protezione utilizza
WindowManager.LayoutParams.FLAG_SECURE. Quando attiva, screenshot e registrazioni dello schermo sono bloccati e l'anteprima dei recenti viene nascosta. dimBackground: truemostra un overlay oscurato nell'app switcher. Confalse, il plugin mostra la tua splash drawable se disponibile, altrimenti applica l'oscuramento.privacyModeOnActivityHiddencontrolla cosa viene mostrato quando l'activity è coperta da altro, ad esempio dal prompt biometrico di sistema:'none'(default),'dim'o'splash'.- La vecchia
preventScreenshotsopzione è deprecata e viene ignorata.FLAG_SECUREviene sempre applicata quando la protezione è attiva. Per permettere uno screenshot in una schermata, chiamadisable()prima di esso eenable()dopo.
iOS
- Quando l'app torna attiva, il plugin aggiunge un overlay per evitare che lo snapshot dell'app switcher mostri i tuoi contenuti.
blurEffect: 'light'o'dark'usa il blur nativo.'none'(default) mostra la launch screen se disponibile, altrimenti uno sfondo di sistema.- While enabled, app content is also placed in a secure rendering layer. Treat iOS screenshot blocking as best effort: Apple does not offer an official API to block screenshots, and behavior can change between iOS versions.
Web
L'implementazione web traccia solo il flag abilitato, così il tuo code gira ovunque. I browser non possono nascondere le anteprime delle schede o bloccare gli screenshot.
Combinalo con la biometria
Un pattern comune per le app banking: sfoca il contenuto nel task switcher, poi richiedi Face ID o impronta digitale quando l'app viene riaperta dopo un po'. Il plugin privacyModeOnActivityHidden mantiene il contenuto coperto mentre è attivo il prompt biometrico di 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' });
}
});
Guarda il plugin biometrico nativo per setup e gestione errori.
Il plugin ufficiale e il plugin Capgo
Il team Capacitor pubblica anche @capacitor/privacy-screen con un API molto simile (enable, disable, isEnabled e le stesse opzioni di piattaforma). Il plugin Capgo mantiene quel API e aggiunge:
enabled: truenella config Capacitor, così la protezione è attiva prima che qualsiasi JavaScript venga eseguito. Il plugin ufficiale si abilita solo da JavaScript.- rendering sicuro dei contenuti su iOS quando attivo, documentato come parte del comportamento del plugin, oltre all'overlay nell'app switcher.
getPluginVersion()per la diagnostica.- Un'implementazione web che mantiene il flag di attivazione, così lo stesso code gira in una preview nel browser.
Se usi già il plugin ufficiale, il passaggio consiste quasi solo in un cambio di import.
Dettagli UX che contano
- Non bloccare gli screenshot ovunque senza motivo. Gli utenti scattano screenshot di ricevute, codici di conferma e impostazioni. Proteggi le schermate con dati sensibili, non l'intera app, a meno che la tua policy di sicurezza non lo richieda.
- Spiega il perché agli utenti. Se su Android un utente prova a fare uno screenshot e ottiene un'immagine nera, una breve nota nel tuo centro assistenza ridurrà i ticket di supporto.
- Testa la condivisione dello schermo. Videochiamate e casting sono bloccati su Android mentre
FLAG_SECUREè impostata. Se la tua app prevede la funzione “condividi schermo con il supporto”, disabilita la protezione per quel flusso. - Controlla l'accessibilità. Gli screen reader continuano a funzionare con
FLAG_SECURE, ma testa comunque con TalkBack e VoiceOver.
Checklist per i test
- Apri una schermata sensibile e vai nell'app switcher. Il contenuto deve essere nascosto su entrambe le piattaforme.
- Su Android, prova uno screenshot e una registrazione schermo. Entrambi devono risultare neri o bloccati.
- Attiva un prompt biometrico e controlla lo sfondo.
- Torna da una schermata sensibile a una normale. La protezione deve disattivarsi se usi la modalità per singola schermata.
- Chiudi e riavvia l'app. Con
enabled: truein config, il primo snapshot deve essere protetto. - Testa su iPad con Split View e Slide Over. L'app potrebbe rimanere visibile quando non è attiva.
Risoluzione problemi
Lo snapshot mostra ancora il contenuto su iOS. Hai attivato la protezione dopo che l'app è passata in background, o solo su alcune rotte. Usa l'opzione di config per proteggere l'intera app.
Schermata nera nelle App Recenti su Android invece della splash screen. Imposta dimBackground: false e assicurati che il tuo splash drawable esista, altrimenti il plugin ripiega sul dimming.
Gli screenshot funzionano ancora su Android. Verifica isEnabled(). Un'altra parte della tua app potrebbe chiamare disable() durante la navigazione.
Protezione persa dopo un aggiornamento OTA. I valori di configurazione sono nativi. Se cambi plugins.PrivacyScreen nella capacitor.config.ts, rilascia una nuova build sugli store. A runtime enable() e disable() le chiamate nel tuo JavaScript possono essere modificate con Live updates di Capgo.
Altri approfondimenti sulla sicurezza
La privacy screen è solo uno degli elementi di una checklist di sicurezza mobile. Considera anche lo storage sicuro, il certificate pinning e il root detection. Capgo offre plugin per SSL pinning e root e jailbreak detection, e il nostro post su regole sulla privacy di Apple per le app Capacitor copre la parte relativa alla revisione. Tutte le opzioni del plugin sono nella documentazione di Privacy Screen.