Saltar al contenido principal

Capacitor Privacy Screen: Oculta el contenido de tu app

Añade una Capacitor privacy screen para ocultar el contenido de la app en el switcher y bloquear capturas, con control por pantalla, configuración y detalles por plataforma.

Ilustración de ajustes de seguridad para añadir una privacy screen a una app Capacitor

Una Capacitor privacy screen oculta el contenido de tu app al abrir el app switcher y, en Android, bloquea capturas de pantalla y grabación de pantalla. Sin ella, iOS y Android toman un snapshot de la última pantalla, así que un saldo bancario, un registro médico o un code sigue visible en la lista de apps recientes. @capgo/capacitor-privacy-screen se añade con una sola llamada o un flag de configuración y te permite proteger solo las pantallas que lo necesiten.

Lo que expone el selector de apps

Cuando un usuario sale de tu app, ambos sistemas capturan una imagen de la pantalla actual:

  • iOS toma una captura cuando la app deja de estar activa para mostrarla en el selector, y brevemente al reiniciarse.
  • Android muestra una vista previa en vivo o en caché en Recientes.

Cualquiera que mire el teléfono verá esa captura. También quien comparta pantalla en una llamada o un usuario que grabe su pantalla para un ticket de soporte. Las capturas también se sincronizan con librerías de fotos en la nube, que es donde realmente se filtran los datos sensibles.

¿Qué apps lo necesitan?

Tipo de app Qué puede filtrarse
Banca, fintech, crypto Saldos, números de tarjeta, IBAN, transacciones
Gestores de contraseñas, autenticadores Contraseñas, códigos TOTP, llaves de recuperación
Salud y datos médicos Diagnósticos, recetas, resultados de laboratorio
Mensajería Conversaciones privadas
RRHH, nóminas, legal Salarios, contratos
Citas, contenido privado Fotos y matches

Las auditorías de seguridad y los penetration tests en estas categorías suelen señalar la falta de protección en el app switcher. OWASP MASVS incluye controles para datos sensibles expuestos en la UI, y los auditores prueban precisamente esto.

Instalar

bun add @capgo/capacitor-privacy-screen
bunx cap sync

El plugin es compatible con Capacitor 8 con la versión 8.x.

Opción 1: proteger toda la app al iniciar

Configúralo en capacitor.config.ts. El plugin nativo lo lee al cargar, antes de que se ejecute tu JavaScript, así que incluso la primera captura está protegida:

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;

Ejecuta bunx cap sync tras cambiar la configuración. La enabled opción se lee solo desde la configuración de Capacitor, no es una opción de runtime.

Opción 2: proteger pantallas específicas

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();

Conéctalos a tu router para cubrir cada ruta de entrada y salida. Con Vue Router, por ejemplo:

router.afterEach(async (to) => {
  if (to.meta.sensitive) {
    await PrivacyScreen.enable();
  } else {
    await PrivacyScreen.disable();
  }
});

Con Angular, usa un guard o una NavigationEnd subscription. Con React Router, un hook en tu layout component funciona. Lo importante es que la navegación hacia atrás, los deep links y el cambio de tabs sigan la misma lógica; de lo contrario, una pantalla sensible podría quedar desprotegida.

Qué hace cada opción

Android

  • La protección usa WindowManager.LayoutParams.FLAG_SECURE. Mientras esté activa, se bloquean las capturas de pantalla y grabaciones, y se oculta la vista previa en Recientes.
  • dimBackground: true muestra una capa oscura en el selector de apps. Con false, el plugin muestra tu splash drawable si está disponible; si no, aplica un efecto de oscurecimiento.
  • privacyModeOnActivityHidden controla qué se muestra cuando tu activity se oculta por otra cosa, como el prompt biométrico del sistema: 'none' (por defecto), 'dim' o 'splash'.
  • El antiguo preventScreenshots esta opción está obsoleta y se ignora. FLAG_SECURE se aplica siempre que la protección esté activa. Para permitir una captura en una pantalla, llama a disable() antes de esto y enable() después.

iOS

  • Cuando la app vuelve a estar activa, el plugin añade una capa para que la captura del app switcher no muestre tu contenido.
  • blurEffect: 'light' o 'dark' usa blur nativo. 'none' (por defecto) muestra la launch screen si está disponible; de lo contrario, un fondo del sistema.
  • Al estar activado, el contenido de la app también se coloca en una capa de renderizado segura. Considera el bloqueo de screenshots en iOS como un 'best effort': Apple no ofrece un API oficial para bloquear screenshots y el comportamiento puede variar entre versiones de iOS.

Web

La implementación web solo rastrea el flag de activación para que tu code funcione en todas partes. Los navegadores no pueden ocultar las vistas previas de las pestañas ni bloquear screenshots.

Combínalo con biometría

Un patrón común en apps bancarias: aplica blur en el switcher y luego solicita Face ID o huella dactilar cuando la app se reanuda tras un tiempo. El plugin privacyModeOnActivityHidden mantiene el contenido cubierto mientras el biometric prompt de Android está visible.

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' });
  }
});

Consulta la plugin de biometría nativa para la configuración y el manejo de errores.

El plugin oficial y el plugin Capgo

El equipo de Capacitor también publica @capacitor/privacy-screen con un API muy similar (enable, disable, isEnabled y las mismas opciones de plataforma). El plugin Capgo mantiene ese API y añade:

  • enabled: true en la config de Capacitor, así la protección se activa antes de que se ejecute cualquier JavaScript. El plugin oficial solo se habilita desde JavaScript.
  • renderizado seguro de contenido en iOS mientras está activo, documentado como parte del comportamiento del plugin, además del overlay en el app switcher.
  • getPluginVersion() para diagnósticos.
  • Una implementación web que mantiene el flag habilitado, para que el mismo code se ejecute en una vista previa del navegador.

Si ya usas el plugin oficial, el cambio es básicamente modificar los imports.

Detalles de UX que importan

  • No bloquees capturas de pantalla sin motivo. Los usuarios capturan recibos, confirmaciones y ajustes. Protege solo pantallas con datos sensibles, no toda la app, a menos que tu política de seguridad lo exija.
  • Explica el porqué a los usuarios. Si alguien intenta hacer una captura en Android y obtiene una imagen negra, una nota breve en tu centro de ayuda evitará tickets de soporte.
  • Prueba el uso compartido de pantalla. Las videollamadas y el casting se bloquean en Android mientras FLAG_SECURE está configurado. Si tu app tiene un flujo de “compartir pantalla con soporte”, desactiva la protección para ese proceso.
  • Verifica la accesibilidad. Los lectores de pantalla siguen funcionando con FLAG_SECURE, pero pruébalos con TalkBack y VoiceOver de todos modos.

Checklist de pruebas

  1. Abre una pantalla sensible y ve al selector de apps. El contenido debe ocultarse en ambas plataformas.
  2. En Android, intenta hacer una captura o grabación de pantalla. Ambas deben verse negras o bloqueadas.
  3. Activa un prompt biométrico y revisa el fondo.
  4. Navega de una pantalla sensible a una normal. La protección debe desactivarse si usas el modo por pantalla.
  5. Cierra y reinicia la app. Con enabled: true en la config, la primera captura debe estar protegida.
  6. Prueba en un iPad con Split View y Slide Over. La app podría seguir siendo visible sin estar activa.

Solución de problemas

La captura aún muestra contenido en iOS. Activaste la protección cuando la app ya estaba en segundo plano, o solo en algunas rutas. Usa la config para proteger toda la app.

Pantalla negra en Recientes en Android en lugar del splash. Configurar dimBackground: false y asegúrate de que tu splash drawable exista; de lo contrario, el plugin usará el oscurecimiento por defecto.

Screenshots still work on Android. Verificar isEnabled(). Otra parte de tu app podría llamar a disable() al navegar.

Protección perdida tras una actualización OTA. Los valores de configuración son nativos. Si cambias plugins.PrivacyScreen en capacitor.config.ts, lanza una nueva versión a la tienda. Runtime enable() y disable() las llamadas en tu JavaScript se pueden cambiar con actualizaciones Live Update de Capgo.

Una pantalla de privacidad es un elemento más en la lista de seguridad móvil. Revisa también el almacenamiento seguro, certificate pinning y root detection. Capgo tiene plugins para SSL pinning y detección de root y jailbreak, y nuestro post sobre las reglas de privacidad de Apple para apps Capacitor explica la parte de revisión. Las opciones completas del plugin están en la documentación de privacy screen.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.