Prevent copy & paste into other iOS apps

Prevent copy & paste into other iOS apps

Advanced clipboard protection on iOS

Β·

4 min read

Especially for enterprise apps it is essential to protect sensitive information by preventing end-users from copying & paste the content into other apps.

In this blog post, I show you multiple ways to introduce such kind of advanced clipboard protection for your iOS app.

No code instrumentation is needed when using MDM

Apple @ Work introduced Managed Pasteboard in iOS 15 which allows IT administrators to apply restrictions to the copy & paste functionality, meaning that information copied from corporate apps cannot be pasted into unmanaged apps and/or the reverse.

Code instrumentation within your app

Let's start by explaining that Apple uses the term pasteboard instead of clipboard. Also, it is essential to know that there are two kinds of pasteboards:

  • systemwide general pasteboard: for sharing data with any app. Persistent across device restarts and app uninstalls. Can be obtained by using UIPasteboard.general

  • custom / named pasteboards: for sharing data with another app/extension (having the same team ID as the app to share from) or with the app itself. Non-persistent by default. Such can be created with UIPasteboard.pasteboardWithName:create: or UIPasteboard.pasteboardWithUniqueName.

Good start: Clear systemwide general pasteboard

Allowing a user to copy/paste within our iOS application and preventing the user from pasting into other apps.

When the user attempts to switch apps then it is possible to get notified through the NotificationCenter and then clear, i.e. override, the clipboard.

NotificationCenter.default.addObserver(
    self,
    selector: #selector(appMovedToBackground),
    name: UIApplication.willResignActiveNotification,
    object: nil
)

@objc
func appMovedToBackground() {
    UIPasteboard.general.string = ""
}

A drawback of this technique is that critical information might stay on the clipboard for a long time and attackers might read out the clipboard by periodically querying its content. Also Apple's handoff feature Universal Clipboard may automatically transfer content to other devices and therefore increases the attack vector.

Using the systemwide general pasteboard should be avoided when dealing with sensitive data.

P.S.: It is not an option to listen to UIPasteboard.changedNotification and immediately clear the clipboard because this would prevent the user from copying & paste within the app.

Better: Ensure the use of custom pasteboard through swizzling

The goal is to return a custom UIPasteBoard whenever the system-wide pasteboard (UIPateboard.general) is requested. Using Swizzling guarantees that the custom pasteboard is always used. Even if the end-user uses the build-in copy capability from UI elements like UITextField that would normally use the system-wide pasteboard.

I'll demonstrate two swizzling techniques

  • Objective-C Runtime

  • Swift Native with @_dynamicReplacement

Swizzling with Objective-Runtime

class AppDelegate: NSObject, UIApplicationDelegate {
    static var privatePasteboard = UIPasteboard.withUniqueName()

    func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        swizzleUIPasteboardGeneral()
        return true
    }

    func swizzleUIPasteboardGeneral() {
        let aClass: AnyClass! = object_getClass(UIPasteboard.general)
        let targetClass: AnyClass! = object_getClass(self)

        let originalMethod = class_getClassMethod(aClass, #selector(getter: UIPasteboard.general))
        let swizzledMethod = class_getInstanceMethod(targetClass, #selector(privatePasteboard))

        if let originalMethod, let swizzledMethod {
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }

    @objc
    func privatePasteboard() -> UIPasteboard {
        return AppDelegate.privatePasteboard
    }
}

On application start the original implementation of UIPasteboard.general gets replaced with an implementation that returns a UIPasteboard that was created with UIPasteboard.withUniqueName()

Swift Native swizzling

Easier is the native Swift swizzling technique by using experimental Swift Attribute @_dynamicReplacement(for: targetFunc(label:))

    extension UIPasteboard {
        @_dynamicReplacement(for: generalPasteboard)
        static var privatePasteboard: UIPasteboard {
            return AppDelegate.privatePasteboard
        }
    }

The code replacement happens at the program start (or loading a shared library), instead of at an arbitrary point in time.

For a deep dive into Swift's native swizzling technique, I recommend the following article:

You can try both techniques in a sample application I published on GitHub.

The private UIPasteboard gets honored by WKWebView and UIWebView so the solution works for native controls as well as web views.

Another important piece of information, in case it is not obvious, the user will also not be able to paste content from other apps into the app.

Conclusion

By using a non-persistent, custom pasteboard, you can reduce the risk that attackers might steal sensitive information.

By using swizzling, you can guarantee that the custom pasteboard is always used. Even if the end-user uses the build-in copy capability from UI elements like UITextField.

Shoutout to Hacktricks iOS Pentesting article about iOS UIPasteboard!

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!