Prevent copy & paste into other iOS apps
Advanced clipboard protection on iOS
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:
orUIPasteboard.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!