A Brief Field Guide to Post-UDID Unique IDs on iOS
In iOS 5.0, the call to retrieve the device-specific unique identifier (“UDID”) of an iOS device — specifically, the accessor to UIDevice’s uniqueIdentifier property — was officially marked as deprecated. This probably wasn’t much of a surprise to anyone involved in mobile privacy and application development. For over a year, researchers have been pointing out numerous instances in which popular mobile applications exfiltrate device-specific data to remote sites, sometimes without encryption. This often includes the UDID, but also can include the device’s model information (or more personal data, like address book information). Some examples of this research are the PiOS project and Eric Smith’s 2010 paper. Although the deprecation was a warning to developers that they shouldn’t rely on uniqueIdentifier in the future, the call was still present and implemented in the API. It remains so to this day. Around the same time, Apple also directed some more pointed warnings at some large developers, asking them to move away from using the global ID.
More recently (in March), App Store review teams began to reject applications that used this method. Although you’re free to use it during development and in applications distributed using ad-hoc or in-house distribution (e.g. pushed from a Mobile Device Management server), this call is no longer allowed in the App Store. There is now no way for applications to retrieve an ID that is both globally available to all applications and guaranteed to be unique across all iOS devices. (The MAC address of the primary wireless device comes close, but isn’t guaranteed; see below). Undoubtedly this made some privacy advocates and device users happy, as there will no longer be an immutable key for application publishers and mobile ad/analytics networks to track. However, those unique-ID users promptly went to work in creating replacement ID schemes that they could use to track a device across multiple, not-necessarily-related applications. This is a gap that needed to be filled quickly for many businesses in the mobile space; if you’re interested in the details of how these IDs are used, a good writeup of one use case recently appeared in this post on the MoPub blog. The short version: they use(d) UDIDs to correlate new application installations or in-app purchases with previous ad clicks. This is worth pointing out, because it’s at the heart of their business, and other companies like it: the need to track devices in a persistent way did not disappear along with the API call.
This post is about ongoing developments in the device-wide ID space, with a special focus on two dueling schemes and codebases that have gained users and attention: OpenUDID and SecureUDID. If you’re an iOS developer, this could (hopefully) serve as a quick introduction to the details of these systems, including their limitations and potential for information leakage.
Problem statement: The challenges of creating a new unique ID
Developing an alternative to the UDID isn’t as straightforward as it might seem to someone unfamiliar with modern mobile-app environments. Although there’s anecdotal evidence that some ad networks are moving towards fingerprinting/time-correlation/IP collection techniques to track events, others are merely choosing a new scheme to generate an ID and using their existing backend infrastructure. We can place these new schemes into two general categories: those that try to derive an ID value from unique data that is already globally accessible on the device; and those that generate a new ID and publish it to other applications using a particular scheme-specific storage protocol.
The former approach was a natural leap for developers. Article comments, blog posts, and Stack Overflow answer suggested forms of this soon after the uniqueIdentifier deprecation. In fact, the ODIN working group’s approach is quite simple: it is merely a SHA-1 hash of the raw MAC address of en0, which is present on every iDevice. (Specfically, their ID is the uppercase-hex-string format of the SHA1 hash. See http://code.google.com/p/odinmobile/source/browse/Sample%20Code/iOS/trunk/ODIN.m.) Others SHA1 over a message containing the MAC address and an app-specific ID — generally the application’s bundle ID. Here’s an example of that approach. These solutions, while clever, often do a poor job of protecting the MAC itself; if an attacker can capture MAC addresses from a network on which mobile devices are joined, he or she can correlate those with UDIDs stored and saved elsewhere. Even if the schemes involved hashes more computationally expensive than a single round of SHA1, the space for a dictionary attack against all Apple OUI prefixes is quite small. The current OUI list, as of 4/25/2012, includes 147 prefixes assigned to “Apple Inc” or “Apple Computer” (or some variation thereof). Each of those allows for a maximum of 2^24 = 16777216 MAC addresses, or ~2.47 * 10^9 addresses overall. Furthermore, this scheme doesn’t afford users control over their IDs — the ability to reset them entirely, as is possible with cookies, browser local storage, and Flash cookies. (As research like Evercookie shows, battling persistent fingerprinting on a mobile device is child’s play compared to trying to stop a modern desktop browser from being tracked by a determined attacker/ad network with sufficient reach.)
The second approach that UDID replacement schemes use — generating an ID and publishing it to other applications — isn’t perfect either. These schemes work like this:
- Look for an ID published by any other application. If one exists, use it.
- Otherwise, generate a new ID. Publish the new ID, then use it.
- (optional) Cache the ID being used to app-local storage (filesystem, NSUserDefaults, keychain, etc.)
All that’s left to work out is the publishing mechanism. Effective sandboxing allows for very few reliable ways for iOS applications to exchange data on a device, particularly when the applications aren’t running at the same time. The two primary supported mechanisms for persistent, bidirectional information exchange that don’t involve external services are keychain items and the pasteboard. The kSecAttrAccessGroup keychain query option requires applications to share a common publisher and application-ID prefix; because of this, keychain sharing isn’t a general solution for storing a global ID. Custom URL schemes, used by many applications as a go-to for unidirectional IPC, wouldn’t work here either — there’s no way to get data back from a call to UIApplication -openURL:.
All that’s left is custom pasteboards. On iOS, UIPasteboards are used not only for sharing cut-and-paste data between applications, but also as a general key-value store accessible by all applications. They’re even persistent across phone reboots, and the application that created a pasteboard doesn’t need to be running for it to be accessible to other applications on a device. However, when the application that created a UIPasteboard item is removed from a device, so is the pasteboard. Because users install and remove applications fairly regularly, using a single location to store your ID isn’t wise: as soon as the user deletes the application that created the ID, the pasteboard disappears. This means that schemes can’t use a single, global UIPasteboard to store their ID; they must diversify.
Before the examples, a quick note on the above. Although the documentation does say that pasteboards created by an application are to be removed on application uninstallation, we’ve failed to observe this after trying multiple applications on a real device. In all cases, a UIPasteboard marked as persistent persists even after its creator is removed and a system reboot occurs (iPhone 4S, iOS 5.0.1). If you’re an iOS developer, keep this in mind. It looks like we’re not the only ones to observe this, either. And in any case, UIPasteboard entries are globally mutable, with exceptions for the special system pasteboards. Any other application may read, write, or remove any other entry at will.
Now, let’s take a look at some real-world examples.
OpenUDID is designed as a drop-in replacement for UIDevice -uniqueIdentifier — a small API that provides a device-wide unique ID, that is shared across all applications that use the OpenUDID scheme. OpenUDID uses a 160-bit ID. The first 128 bits are the result of a single round of MD5 on the return value of NSProcessInfo -globallyUniqueString, which is derived from the device’s hostname (typically based off of the owner’s name), the pid of the running app, and a timestamp (docs here). The latter 32 bits are the output of arc4random(). Like the original UDID, this is retrieved by the OpenUDID API as an NSString in lowercase-hex format.
OpenUDID’s persistence is achieved using the UIPasteboard, as discussed above. To ensure persistence past application removal, OpenUDID doesn’t use a single custom pasteboard item, but many custom pasteboard items — 100, to be exact — named “org.OpenUDID.slot.N”, where N is [0, 99]. Each application that uses OpenUDID chooses one of these “slots” (saving its choice locally in NSUserDefaults), and saves a copy of its idea of the current OpenUDID inside it. These slots are all public, so if there’s no cached OpenUDID, the code goes through all 100 slots and chooses the UDID that is represented most frequently. If and only if all slots are empty (or corrupted), a new OpenUDID is generated by the scheme listed above.
OpenUDID’s optout functionality works by setting a value in the NSDictionary stored inside the pasteboard slot for a given application. This value is not propagated from other applications’ slots, like the UDID is. For this reason, there’s no global opt-out functionality respected by OpenUDID. However, since UIPasteboard objects are mutable, and the data in the slots are unencrypted, it’s possible for another application to rewrite all populated UIPasteboard slots with values that include opt-out functionality.
SecureUDID’s design goals are a bit different from OpenUDID and the original UIDevice UDID. Instead of providing a common ID shared across all applications, SecureUDID shares an ID across multiple applications that share a secret. The ID is encrypted using the secret as the key; the secret is then embedded in the binary, and ostensibly shared across all other users of your ID. This is intended to limit the spread of tracking applications to those authorized by a specific subset of SecureUDID users.
Persistence: Like OpenUDID, SecureUDID uses UIPasteboard “slots” (64 of them) to store copies of an ID along with some metadata. Unlike OpenUDID, it doesn’t claim one slot per application — instead, it’s one application per secret. This potentially has implications for persistence: if the application that created the UIPasteboard entry is removed, the UIPasteboard documentation says that the slot will be removed as well. (As noted above, in practice this doesn’t happen, but it might in the future.)
SecureUDID’s IDs are generated by CFUUIDCreate() which results in a 128-bit value that is derived from hardware values (including, likely, the MAC address, according to the documentation) but guaranteed to be unique. It also features opt-out functionality, but SecureUDID honors opt-out flags from all other SecureUDID slots, even those which aren’t using an ID encrypted with the (domain,key) pair used by the checking application. For users, this means that if you opt out of SecureUDID in one location, this will propagate to other applications that use SecureUDID as well.
SecureUDID’s secret (the crypto key) is a SHA1 hash of the concatenation of two values, both passed to SecureUDID’s +UDIDForDomain:usingKey: method. Although the documentation implies that one of these is intended to be public knowledge (bundle ID) and the other is a private key, the effective secrecy of these values is identical if they’re both embedded in the binary. If this is the case, then they are visible to anyone with a copy of the application’s binary or a running device. As such, there are no technical barriers preventing unauthorized applications from using these secrets to decrypt and read SecureUDIDs created by from existing, published applications.
Another interesting feature of SecureUDID is that its pasteboard slots (“org.secureudid-N”, where N is [0, 63]) store an unencrypted NSDate value that contains the last time a given key was accessed. All applications that use a given SecureUDID secret update this value; it’s used to evict an existing UIPasteboard slots when all 64 are full. Since this value is global, it means that unscrupulous developers could write applications that can use this value to nefarious purposes — effectively getting information on how often applications that share a common secret are used– even without knowing the key or decrypting the SecureUDID.
It’s worth pointing out that these schemes are still very much in development. In particular, opt-out functionality is still being ironed out, particularly with respect to UI options. Even though versions of these schemes are in shipping code today, this may change in the future. However, the fundamental challenges remain, as will analytics/advertising networks’ need for tracking.
Still, it’s difficult to say whether there’s any net change here. Although an easy-to-use (and much-berated) unique ID mechanism is now gone, some companies’ need to track users across multiple applications has not changed. It’s relatively easy for ad or analytics networks to get their users to incorporate new schemes like those dissected here — in fact, it’s standard operating procedure to hand users a bundle of code that’s fairly easy to integrate. Users can expect to see variations on these tracking schemes for the foreseeable future. Technical changes (such as Apple’s decision to remove the call) can only go so far without breaking much-desired features — at some point, the responsibility lies with individual application publishers to decide exactly what kind of tracking/identification technology they decide to use. These publishers are ultimately the ones that decide what goes in applications that people use, and how much control users have over their personal usage data. As for savvy users, they’re left with limited options: in some cases, learning about these schemes opens up avenues for interference (e.g. write an application that deletes or rewrites all tracking-related UIPasteboard items); in others, the only option is to either accept some amount of usage tracking or not use certain applications at all.