Skip to content
  • There are no suggestions because the search field is empty.

Securing User-Level Functions in iOS Applications

Defending iOS user interactions against screen capture, biometric bypass, keylogging, and tapjacking

Overview

iOS provides strong platform isolation through its sandboxed execution model. However, an application running on an untrusted or compromised device—such as a jailbroken device, a device subject to runtime instrumentation, or a manipulated development environment—cannot rely solely on client-side controls.

Sensitive user interactions can be targeted through:

  • Screen capture, screen recording, and app-switcher snapshots
  • Biometric-authentication bypass through runtime method hooking
  • Keystroke, clipboard, and keyboard-extension interception
  • Tapjacking, injected windows, and UI-overlay manipulation

A resilient implementation combines native iOS security capabilities with runtime application self-protection (RASP), remote attestation, and server-side API enforcement. Approov Mobile App and API Protection can provide the runtime and backend enforcement layer, helping ensure that a compromised client cannot continue to access protected APIs merely by bypassing local UI controls.

Native controls reduce exposure in the user interface. Remote attestation and API enforcement ensure that bypassing those controls does not automatically grant access to sensitive backend operations.

Screen Capture and Recording Protection

Threat

Attackers may capture confidential application content through screen recording, AirPlay or external-display mirroring, background app-switcher snapshots, or unauthorized diagnostic and instrumentation tools.

Typical targets include:

  • Authentication screens
  • Payment and banking data
  • Personal health information
  • Recovery codes and one-time passwords
  • Account numbers and personally identifiable information

Native iOS Controls

Unlike Android, iOS does not provide a direct equivalent to FLAG_SECURE. Instead, applications should combine the following protections:

  • Detect active screen capture or mirroring with UIScreen.capturedDidChangeNotification
  • Obscure sensitive content when the application resigns active status
  • Remove the obfuscation only after the application returns to the foreground and the screen is not captured
  • For especially sensitive content, evaluate secure view-layering patterns that use secure text-entry rendering behavior

Swift example: detect capture and obscure app-switcher content

swift
import UIKit
final class SecureViewController: UIViewController {
private var blurEffectView: UIVisualEffectView?
override func viewDidLoad() {
super.viewDidLoad()
configureScreenCaptureObserver() configureApplicationStateObservers()
}

deinit {
NotificationCenter.default.removeObserver(self)
}

private func configureScreenCaptureObserver() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleScreenCaptureChange),
name: UIScreen.capturedDidChangeNotification,
object: nil
)
handleScreenCaptureChange()
}
@objc private func handleScreenCaptureChange() {
DispatchQueue.main.async {
if UIScreen.main.isCaptured {
self.applyScreenBlur()
} else {
self.removeScreenBlur()
}
}
}

private func configureApplicationStateObservers() {
NotificationCenter.default.addObserver(
forName: UIApplication.willResignActiveNotification,
object: nil,
queue: .main
) { [weak self] _ in
self?.applyScreenBlur()
}
NotificationCenter.default.addObserver(
forName: UIApplication.didBecomeActiveNotification,
object: nil,
queue: .main
) { [weak self] _ in
if !UIScreen.main.isCaptured {
self?.removeScreenBlur()
}
}
}
private func applyScreenBlur() {
guard blurEffectView == nil else { return }
let blurEffect = UIBlurEffect(style: .dark)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = view.bounds
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(blurView)
blurEffectView = blurView
}
private func removeScreenBlur() {
blurEffectView?.removeFromSuperview()
blurEffectView = nil
}
}

 

Approov role

A determined attacker may use runtime instrumentation tools to disable notification observers, modify UIScreen.main.isCaptured, or alter UI behavior. Local detection alone is therefore insufficient.

Approov can add a server-enforced control layer by:

  • Detecting compromised or instrumented runtime environments
  • Identifying jailbreak indicators and dynamic-analysis tooling
  • Issuing short-lived attestation tokens only to trusted application instances
  • Preventing API access when app integrity or runtime trust checks fail

This approach means that bypassing a visual blur locally does not, by itself, grant access to protected backend data or transactions.

Implementation notes

  • Treat screen-capture detection as a response mechanism, not as a guarantee of screenshot prevention.
  • Do not display secrets, private keys, or long-lived session credentials unnecessarily in the UI.
  • Ensure blur views cover all sensitive windows and scenes in multi-window applications.
  • Test behavior for app switching, Control Center, screen recording, AirPlay, and external displays.

Biometric Authentication Integrity

Threat

A common biometric-bypass technique uses dynamic instrumentation to hook LAContext.evaluatePolicy(_:localizedReason:reply:) and force the completion handler to report success.

If the application treats a boolean callback as the sole proof of user authentication, an attacker may bypass Face ID or Touch ID without a valid biometric match.

Native iOS Controls

Do not use biometric success as a standalone authorization decision for high-value actions. Instead, bind sensitive operations to a cryptographic key protected by the Secure Enclave.

Use:

  • SecAccessControlCreateWithFlags
  • kSecAttrTokenIDSecureEnclave
  • .biometryCurrentSet for stronger binding to the currently enrolled biometric set
  • Hardware-backed signing or decryption operations that require biometric authorization

With this pattern, a hooked software callback cannot produce the cryptographic operation that the backend requires.

Swift example: create a biometric-protected Secure Enclave key

swift
import LocalAuthentication
import Security
final class SecureEnclaveBiometricManager {
func generateBiometricKey() throws -> SecKey? {
var error: Unmanaged<CFError>?
guard let accessControl = SecAccessControlCreateWithFlags(
kCFAllocatorDefault, 
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .biometryCurrentSet, &error
) else {
if let error = error?.takeRetainedValue() { throw error
}
return nil
}
let applicationTag = "com.example.app.biometricAuthKey" .data(using: .utf8)!
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: applicationTag,
kSecAttrAccessControl as String: accessControl
]
]
return SecKeyCreateRandomKey(attributes as CFDictionary, &error)
}
}

Recommended authorization pattern

For sensitive backend operations:

  1. The app requests biometric authorization.
  2. A Secure Enclave-protected private key signs a server-issued, short-lived challenge.
  3. The app submits the signature with an Approov attestation token.
  4. The backend verifies the cryptographic signature, challenge freshness, device/application integrity, and authorization policy.
  5. The backend executes the protected operation only if all checks pass.

This avoids treating a local success == true result as sufficient proof of identity.

Approov role

Approov can complement Secure Enclave-backed authentication by enforcing remote trust requirements before the backend accepts a request.

Potential controls include:

  • Application and device integrity verification
  • Attestation-token validation at the API gateway
  • Dynamic secrets or token binding for protected workflows
  • Blocking requests from modified, automated, or instrumented application runtimes
Implementation notes
  • Use .biometryCurrentSet when biometric enrollment changes should invalidate existing authorization material.
  • Use ThisDeviceOnly accessibility classes for credentials that must not migrate through backups.
  • Store only key references or identifiers in app storage; keep private-key operations inside the Secure Enclave.
  • Require a fresh, server-issued nonce for every high-value signing event to prevent replay.

Keylogging and Sensitive Input Protection

Threat

On compromised devices, attackers may intercept text input through injected libraries, jailbreak tweaks, custom keyboards, malicious accessibility tooling, clipboard monitoring, or modified text-input frameworks.

Sensitive inputs can include:

  • Usernames and passwords
  • Payment-card data
  • One-time passcodes
  • Recovery phrases
  • API tokens and access credentials

Native iOS Controls

Applications can reduce input exposure by blocking third-party keyboards and disabling text-assistance features on sensitive fields.

Swift example: block third-party keyboards

Add the following to AppDelegate:

swift
func application(
_ application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier:
UIApplication.ExtensionPointIdentifier
) -> Bool {
if extensionPointIdentifier == .keyboard {
return false
}
return true
}

This prevents custom keyboard extensions from being used while the application is active.

Swift example: configure a sensitive text field
swift
import UIKit
let sensitiveTextField = UITextField()
sensitiveTextField.isSecureTextEntry = true sensitiveTextField.autocorrectionType = .no sensitiveTextField.spellCheckingType = .no sensitiveTextField.smartDashesType = .no sensitiveTextField.smartInsertDeleteType = .no sensitiveTextField.autocapitalizationType = .none sensitiveTextField.textContentType = .oneTimeCode

Set textContentType according to the purpose of the field. For example, use an appropriate password-related content type for login fields rather than applying .oneTimeCode universally.

Additional recommendations

  • Avoid copying sensitive values to the pasteboard.
  • Clear sensitive text fields promptly after use.
  • Do not log text-field contents, including in debug logging and analytics events.
  • Avoid recording keystroke timing or full input values in telemetry.
  • Use secure server-side authentication protocols rather than retaining passwords or sensitive secrets locally.
  • Make the product decision to block third-party keyboards intentionally, as it may affect accessibility and user experience.

Approov role

A native configuration cannot fully defend against input interception on a compromised runtime. Approov can help detect hostile execution environments and stop compromised clients from completing sensitive API calls.

This can include identification of:

  • Jailbreak artifacts
  • Injected dynamic libraries
  • Runtime method hooks
  • Modified system frameworks
  • Instrumented app processes

Approov policy updates can also help teams respond to emerging compromise indicators without requiring every policy adjustment to wait for a new application release.

Tapjacking and UI Overlay Prevention

Threat

Tapjacking attempts to cause users to interact with a different interface element than they believe they are selecting. On iOS, this risk may arise from:

  • Injected UIWindow instances
  • Manipulated window levels
  • Floating overlays
  • Picture-in-Picture interactions
  • Runtime UI changes introduced through hooks or jailbreak tweaks
  • Spoofed accessibility layers

Native iOS Controls

Sensitive views should validate the relevant window and view hierarchy before processing security-critical user actions. This is especially important for actions such as approving payments, changing security settings, authorizing device enrollment, or exporting sensitive data.

Swift example: validate the active window stack
swift
import UIKit
final class OverlayProtectedView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard validateWindowStack() else {
return nil
}
return super.hitTest(point, with: event)
}

 

private func validateWindowStack() -> Bool {
guard let scene = window?.windowScene else {
return false
}
let activeWindows = scene.windows.filter { !$0.isHidden }
for activeWindow in activeWindows {
if activeWindow.windowLevel > .normal {
print("Security warning: unexpected high-level window detected.")
return false
}
}
return true
}
}

Important implementation caution

A blanket policy that rejects every window above .normal can interfere with legitimate system UI, accessibility features, alerts, and application-specific windows. Production implementations should define an explicit allowlist of expected windows and validate the hierarchy in the context of the app’s architecture.

For the most sensitive actions, also consider:

  • Requiring explicit user confirmation on a dedicated screen
  • Revalidating authorization immediately before transaction submission
  • Displaying transaction details that are cryptographically bound to the server challenge
  • Rejecting stale, replayed, or context-mismatched approval requests on the backend

Approov role

If an attacker manipulates a UI hierarchy or intercepts a touch flow, the backend should still require proof that the request originated from an authentic, non-tampered application runtime.

Approov can provide that remote enforcement layer by failing attestation and withholding usable API credentials when the runtime environment does not meet integrity requirements.

Protection Architecture

Threat vector Native iOS control Approov protection role Reference
Screen capture and recording Monitor UIScreen.capturedDidChangeNotification; blur on UIApplication.willResignActiveNotification; consider secure view-layering patterns Detect instrumented or compromised runtimes that attempt to suppress capture controls; enforce API access through attestation ScreenProtectorKit
Biometric bypass Secure Enclave keys with SecAccessControl and .biometryCurrentSet Combine device and application integrity enforcement with backend token validation Apple Secure Enclave documentation
Keylogging and input interception Block custom keyboards; disable autocorrect, predictive text, and smart input behavior on sensitive fields Detect jailbreak indicators, injected libraries, and manipulated runtimes before API requests are accepted OWASP MASVS
Tapjacking and UI overlays Validate expected windows, window levels, touch targets, and view hierarchy for critical flows Prevent compromised or UI-manipulated clients from completing protected API transactions OWASP MASVS Resilience

Technical Implementation Checklist

Use this checklist when implementing user-level protections in an iOS application:

  • Monitor UIScreen.capturedDidChangeNotification and respond appropriately when the screen is captured or mirrored.
  • Obscure sensitive content when the app resigns active status to reduce app-switcher snapshot exposure.
  • Use Secure Enclave-backed keys for sensitive biometric authorization flows.
  • Bind biometric authorization to a cryptographic operation rather than relying solely on an LAContext success callback.
  • Use .biometryCurrentSet when new biometric enrollment should invalidate existing authorization material.
  • Disable custom keyboard extensions where the security requirements justify the usability trade-off.
  • Configure sensitive text fields to disable autocorrection, spell checking, predictive input, and smart text features.
  • Avoid placing passwords, secrets, access tokens, or recovery codes on the pasteboard.
  • Validate the active window and touch-routing context before handling high-value interactions.
  • Design backend APIs to reject stale, replayed, or unsigned sensitive requests.
  • Integrate the Approov iOS SDK to apply runtime attestation and enforce trusted application access to backend APIs.
  • Validate Approov tokens and associated integrity signals at the API gateway or backend service layer.
  • Test controls on both standard devices and representative compromised or instrumented test environments.

Key Takeaway

Native iOS controls are essential for reducing exposure at the UI layer, but they should not be treated as the final security boundary. Screen-capture detection, Secure Enclave-backed biometrics, restricted keyboard behavior, and window-integrity checks are most effective when paired with server-side enforcement.

Approov extends these protections beyond the device by enabling runtime integrity checks and remote API enforcement. This creates a layered architecture in which an attacker who bypasses a local UI control still cannot reliably use the application’s protected backend services.