---
title: Che nội dung nhạy cảm khi app vào background để tránh lộ qua App Switcher
impact: HIGH
impactDescription: iOS chụp screenshot app khi vào background để hiển thị trong App Switcher. Màn hình chứa số dư tài khoản, tin nhắn riêng tư, hoặc thông tin cá nhân sẽ bị lưu trong bộ nhớ và có thể bị trích xuất từ device backup trên jailbroken device.
tags: swift, ios, background-snapshot, app-switcher, data-privacy, ui-security, security
---

## Che nội dung nhạy cảm khi app vào background để tránh lộ qua App Switcher

Khi app vào background (`applicationWillResignActive`/`sceneWillResignActive`), iOS chụp snapshot để hiển thị trong App Switcher. Phải che màn hình chứa dữ liệu nhạy cảm bằng cách thêm blur overlay hoặc splash screen trước khi app vào background.

**Incorrect (không che màn hình khi vào background):**

```swift
import UIKit

// !! App không làm gì khi vào background
// Màn hình banking balance, tin nhắn, health data bị capture trong App Switcher snapshot
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    // Không có xử lý background snapshot protection
}

// !! Màn hình hiển thị số dư tiền mà không bảo vệ
class AccountBalanceViewController: UIViewController {
    @IBOutlet weak var balanceLabel: UILabel!  // "$12,345.67" hiển thị trong snapshot!

    override func viewDidLoad() {
        super.viewDidLoad()
        balanceLabel.text = "$12,345.67"
        // Không register notification để hide khi background
    }
}
```

**Correct (blur overlay khi vào background):**

```swift
import UIKit

// SAFE: Scene-based approach (iOS 13+)
class SceneDelegate: UIResponder, UISceneDelegate {
    private var privacyOverlay: UIView?

    func sceneWillResignActive(_ scene: UIScene) {
        addPrivacyOverlay(to: scene)
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        removePrivacyOverlay()
    }

    private func addPrivacyOverlay(to scene: UIScene) {
        guard let windowScene = scene as? UIWindowScene,
              let window = windowScene.windows.first else { return }

        if privacyOverlay != nil { return }  // Đã có overlay

        let overlay = UIView(frame: window.bounds)
        overlay.backgroundColor = .systemBackground
        overlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        // Thêm logo app thay vì màn trắng để đẹp hơn
        let imageView = UIImageView(image: UIImage(named: "AppLogo"))
        imageView.contentMode = .scaleAspectFit
        imageView.center = CGPoint(x: overlay.bounds.midX, y: overlay.bounds.midY)
        overlay.addSubview(imageView)

        window.addSubview(overlay)
        privacyOverlay = overlay
    }

    private func removePrivacyOverlay() {
        privacyOverlay?.removeFromSuperview()
        privacyOverlay = nil
    }
}

// SAFE: Màn hình cụ thể tự bảo vệ
class SensitiveDataViewController: UIViewController {
    @IBOutlet weak var balanceLabel: UILabel!
    private var blurView: UIVisualEffectView?

    override func viewDidLoad() {
        super.viewDidLoad()
        let nc = NotificationCenter.default
        nc.addObserver(self, selector: #selector(appWillResignActive),
                       name: UIApplication.willResignActiveNotification, object: nil)
        nc.addObserver(self, selector: #selector(appDidBecomeActive),
                       name: UIApplication.didBecomeActiveNotification, object: nil)
    }

    @objc private func appWillResignActive() {
        let blur = UIBlurEffect(style: .systemThickMaterial)
        let blurView = UIVisualEffectView(effect: blur)
        blurView.frame = view.bounds
        blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(blurView)
        self.blurView = blurView
    }

    @objc private func appDidBecomeActive() {
        blurView?.removeFromSuperview()
        blurView = nil
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}
```

**Tools:** OWASP MASVS-PLATFORM-4, iMazing (inspect snapshots in backup), Simulator App Switcher testing
