Capacitor privacy screen을 사용하면 사용자가 앱 스위처를 열 때 앱 콘텐츠를 숨길 수 있으며, Android에서는 스크린샷과 화면 녹화를 차단합니다. 이 기능이 없으면 iOS와 Android가 마지막 화면을 스냅샷으로 저장하므로, 최근 사용 앱 목록에 은행 잔고, 의료 기록 또는 일회용 code 정보가 그대로 노출될 수 있습니다. @capgo/capacitor-privacy-screen 단 한 번의 호출이나 설정 플래그만으로 이를 추가할 수 있으며, 보호가 필요한 화면만 선택적으로 적용할 수 있습니다.
앱 스위처에서 노출되는 정보
사용자가 앱을 벗어날 때, 두 OS 모두 현재 화면을 캡처합니다:
- iOS 앱이 비활성화될 때 스냅샷을 찍어 앱 스위처에 표시하며, 앱이 다시 실행되는 동안에도 잠시 노출됩니다.
- Android 최근 앱 목록(Recents)에 실시간 또는 캐시된 미리보기를 보여줍니다.
폰을 힐끗 보는 사람뿐만 아니라, 화면 공유 중인 사용자나 고객 지원을 위해 화면을 녹화하는 사용자에게도 해당 스냅샷이 노출됩니다. 또한 스크린샷이 클라우드 사진 라이브러리와 동기화되면서 민감한 데이터가 유출될 위험이 큽니다.
적용이 필요한 앱 유형
| 앱 유형 | 유출 가능 정보 |
|---|---|
| 뱅킹, 핀테크, 암호화폐 | 잔액, 카드 번호, 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에서는 가드(guard)나 NavigationEnd subscription을 사용하세요. React Router는 레이아웃 컴포넌트의 hook이 적합합니다. 중요한 점은 뒤로 가기, 딥 링크, 탭 전환이 모두 같은 로직을 거쳐야 한다는 것입니다. 그렇지 않으면 민감한 화면이 보호되지 않을 수 있습니다.
각 옵션의 기능
Android
- 보호 기능 사용 시
WindowManager.LayoutParams.FLAG_SECURE. 활성화되면 스크린샷 및 화면 녹화가 차단되며 최근 앱 미리보기가 숨겨집니다. dimBackground: trueshows a dim overlay in the app switcher. Withfalse사용 가능한 경우 플러그인이 splash drawable을 표시하며, 그렇지 않으면 화면을 어둡게 처리합니다.privacyModeOnActivityHidden시스템 생체 인식 prompt 등 다른 요소에 의해 액티비티가 가려질 때 표시될 내용을 제어합니다:'none'(기본값),'dim'또는'splash'.- 기존의
preventScreenshots옵션은 deprecated 되었으며 무시됩니다.FLAG_SECURE보호 기능이 켜져 있는 동안 항상 적용됩니다. 특정 화면에서 스크린샷을 허용하려면 호출하기 전에disable()를 호출하고enable()이후.
iOS
- 앱이 active 상태를 벗어나면 플러그인이 오버레이를 추가해 앱 스위처 스냅샷에 콘텐츠가 표시되지 않도록 합니다.
blurEffect: 'light'또는'dark'네이티브 blur를 사용합니다.'none'(기본값) 런치 스크린이 있으면 표시하고, 없으면 시스템 배경을 표시합니다.- 활성화된 동안 앱 콘텐츠도 보안 렌더링 레이어에 배치됩니다. iOS 스크린샷 차단은 best effort로 간주하세요. Apple은 스크린샷 차단을 위한 공식 API를 제공하지 않으며, iOS 버전에 따라 동작이 달라질 수 있습니다.
웹
웹 구현은 활성화 플래그만 추적하므로 code가 어디서든 실행됩니다. 브라우저는 탭 미리보기를 숨기거나 스크린샷을 차단할 수 없습니다.
생체 인증과 함께 사용하기
금융 앱에서 흔히 쓰는 패턴입니다. 앱 스위처에서는 blur 처리하고, 일정 시간 후 앱으로 돌아올 때 Face ID나 지문 인식을 요구하는 방식입니다. 이 플러그인은 privacyModeOnActivityHidden Android biometric prompt가 떠 있는 동안 콘텐츠를 가려진 상태로 유지합니다.
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()진단용.- 활성화 플래그를 유지하는 웹 구현체로, 브라우저 프리뷰에서도 동일한 code 가 실행됩니다.
이미 공식 플러그인을 사용 중이라면, 전환 시 대부분 import 문만 변경하면 됩니다.
중요한 UX 디테일
- 이유 없이 모든 스크린샷을 차단하지 마세요. 사용자는 영수증, 확인 번호, 설정 등을 스크린샷으로 찍습니다. 보안 정책상 필수적인 경우가 아니라면 앱 전체가 아닌 민감한 데이터가 포함된 화면만 보호하세요.
- 사용자에게 이유를 설명하세요. Android에서 스크린샷을 찍었을 때 검은 화면만 나온다면, 고객 센터에 안내 문구를 남겨 지원 티켓 발생을 줄이세요.
- 화면 공유를 테스트하세요. Video calls and casting are blocked on Android while
FLAG_SECURE앱에 “고객 지원을 위한 화면 공유” 기능이 있다면, 해당 흐름에서는 보호 기능을 비활성화하세요. - 접근성을 확인하세요. 스크린 리더는 여전히 작동합니다
FLAG_SECURETalkBack 및 VoiceOver로 직접 테스트해 보세요.
테스트 체크리스트
- 민감한 화면을 연 상태에서 앱 스위처로 이동합니다. 두 플랫폼 모두에서 콘텐츠가 숨겨져 있어야 합니다.
- Android에서 스크린샷과 화면 녹화를 시도합니다. 둘 다 검은색 화면이거나 차단되어야 합니다.
- 생체 인증 prompt를 실행하고 배경 화면을 확인합니다.
- 민감한 화면에서 일반 화면으로 돌아갑니다. per-screen 모드를 사용 중이라면 보호 기능이 꺼져야 합니다.
- Kill and relaunch the app. With
enabled: true첫 번째 스냅샷도 반드시 보호되어야 합니다. - iPad의 Split View 및 Slide Over 환경에서 테스트합니다. 앱이 비활성 상태일 때도 화면이 보일 수 있습니다.
문제 해결
iOS에서 스냅샷에 여전히 콘텐츠가 노출됩니다. 앱이 이미 백그라운드로 전환된 후 보호 기능을 활성화했거나, 일부 route에만 적용되었습니다. 앱 전체 보호를 위해 config 옵션을 사용하세요.
Android 최근 사용 앱 목록에서 splash 대신 검은 화면이 나타납니다. 설정 dimBackground: false splash drawable이 존재하는지 확인하세요. 그렇지 않으면 플러그인이 화면을 어둡게 처리하는 방식으로 동작합니다.
Android에서는 여전히 스크린샷 촬영이 가능합니다. 체크 항목 isEnabled(). 앱의 다른 부분에서 다음을 호출할 수도 있습니다 disable() 내비게이션 시.
OTA 업데이트 후 보호 기능이 해제되었습니다. 설정 값은 네이티브 기반입니다. 만약 값을 변경하려면 plugins.PrivacyScreen 에서 capacitor.config.ts, 스토어에 새 빌드를 배포하세요. 런타임 enable() 및 disable() JavaScript 호출은 다음을 통해 변경할 수 있습니다 Capgo Live Update 사용하기.
관련 보안 작업
프라이버시 스크린은 모바일 보안 체크리스트 중 하나입니다. 보안 스토리지, 인증서 피닝, 루팅 감지 기능도 함께 확인해 보세요. Capgo는 다음 기능을 위한 플러그인을 제공합니다: SSL 피닝 및 루팅 및 탈옥 감지, 그리고 다음 포스트에서 다루는 Capacitor 앱을 위한 Apple의 프라이버시 규정은 리뷰 측면을 다룹니다. 전체 플러그인 옵션은 프라이버시 스크린 문서에서.