Device Identifiers
Definition
Device identifier is any value your app can read that distinguishes one physical device from another. It covers everything from a hardware-burned serial number to a random UUID you generate yourself and stash in the Keychain.
Identifiers sit on a spectrum of scope:
Device-wide (hardware-scoped) - tied to the physical device (IMEI, serial number, the Widevine DRM ID). One value, shared by every app and every user on the device.
App-scoped / vendor-scoped - the same value only within your app or your publisher's family of apps (Android's
ANDROID_ID, iOS'sidentifierForVendor).Install-scoped - dies when the app is uninstalled (Firebase Installation ID, a UUID in
UserDefaults).
The tighter the scope, the friendlier to privacy, and the less useful it is for the cross-app tracking that regulators, Apple, and Google have spent the last decade dismantling.
What do we use them for?
Device identifiers serve two broad camps. Both want to recognise a device, but they want different things from the identifier, and this shapes which one is a good fit.
Growth and Analysis
Used to collect personalised data and preferences and also to serve targeted ads. This camp cares about counting and attributing activity rather than stopping a determined user, so a resettable identifier is usually enough.
Typical uses:
Measuring unique users and installs.
Attributing an install to the ad or campaign that drove it.
Personalising content, preferences, and recommendations.
Frequency capping and grouping users into A/B test cohorts.
Serving and measuring targeted advertising.
Here the user should be able to opt out, so the platforms push toward resettable, consent-based identifiers. iOS gates the advertising identifier behind App Tracking Transparency, and Android makes the Advertising ID resettable and lets the user opt out. Persistence is not the priority, and long-lived hardware identifiers are usually the wrong tool for this camp.
Security and Integrity
The main goal is to identify a device across sessions (app launches, installations, and even factory resets) so we can block malicious devices. The difference from the analysis camp is the adversary. The person holding the device may be actively trying to change, hide, or forge the identifier, so persistence and spoof-resistance matter far more than reach.
Typical uses:
Blocking a device that has already abused a service.
Preventing promo and sign-up bonus abuse and multi-accounting.
Detecting bot farms, emulators, and automated devices.
Rate limiting and defending against account takeover.
Enforcing licensing and entitlements.
Because the value must survive reinstalls, resets, and deliberate tampering, this camp leans toward hardware-backed and attested signals rather than freely resettable strings. No single freely readable identifier meets every requirement, which is why security use cases often combine several signals.
What ideal identifier contains?
Ideal identifier has following attributes:
Uniqueness - any two devices from the pool of devices shouldn't have colliding value of identifier.
Persistence - identifier should survive any malicious manipulation such as rebooting, reinstallation of the app, factory reset, or OS update.
Resettability - the user can and should be able to wipe it intentionally. A wipeable identifier is better for privacy, but a disadvantage for security.
Coverage - should be available for every device, including different OEMs, older hardware, and emulators.
Privacy - the identifier shouldn't collect sensitive information just for the sake of identifying. The identifier itself shouldn't carry any meaning.
Resistant - it should be impossible to spoof or forge.
Commonly used identifiers
Each identifier below is described with its persistence behavior, its advantages, and its disadvantages. The wording of the persistence bullets is kept consistent so entries can be compared directly.
Android
Android exposes several identifiers with very different persistence and privacy trade-offs. The options range from freely readable strings to hardware-backed attestation.
Quick rundown analysis through Android identifiers Fraud-Proofing an Android App: Choosing the Best Device ID for Promo Abuse Prevention
Android ID
Android ID, also called SSAID, is the default no-permission identifier on Android. It is a 64-bit hex value read via Settings.Secure.ANDROID_ID.
Since Android 8.0 the value is derived from the combination of the app signing key, the user profile, and the device. The same physical device therefore returns a different value to apps signed by different keys, and a different value in each user profile. Apps signed with the same key share the value.
Persistance
Survives reboots and OS updates.
Survives app reinstall while the signing key stays the same.
Reset by factory reset.
Changes if the signing key changes.
Not directly resettable by the user.
Advantages
No permissions required.
Stable for the common case.
Isolated per app, which is privacy-reasonable.
Disadvantages
Reset by factory reset, so a wiped device looks new.
Scoped per user profile, which complicates multi-user and work-profile devices.
Plain string, so it can be spoofed on a rooted or hooked device.
The value is anchored to your signing key. That is reliable when you own the app, but you cannot match it against apps you do not sign.
Advertising ID
The Advertising ID, also called GAID or AAID, is a resettable UUID from Google Play services meant only for ads and profiling. It is device-wide and shared across all apps, which is what makes it suitable for cross-app ad attribution and unsuitable for anything the user is meant to be able to escape. It is read through the Google Play services Advertising ID API.
Persistance
Survives reboots, OS updates, and app reinstall.
Gets a new value on factory reset.
The user can reset it at any time from settings.
Returns a zeroed value when the user opts out of ad personalisation.
Advantages
Cross-app by design.
Available on all Play-certified devices.
Disadvantages
Unstable, because the user can reset it at any time.
Returns no usable value when the user opts out.
Requires the
AD_IDpermission on Android 13 and later.Google policy forbids linking it to other PII or persistent identifiers.
Unavailable on devices without Google Play services.
MediaDRM / Widevine device ID
The interesting one for the security camp. It is a hardware-backed value exposed by the DRM subsystem, read via MediaDrm.getPropertyByteArray(PROPERTY_DEVICE_UNIQUE_ID) against the Widevine UUID. It is provisioned when the device sets up DRM, so it is the same for every app and every user on the device and does not depend on app or OS state. This makes it the most persistent value an app can read without special permissions.
Persistance
Survives reboots, OS updates, and app reinstall.
Usually survives factory reset, because it is rooted in DRM provisioning rather than app or OS state.
Not resettable by the user.
Advantages
No permissions required.
Device-wide, so it is shared across apps and user profiles.
Strongest persistence of any freely readable Android identifier.
Harder to spoof than a settings string.
Disadvantages
Missing on devices without hardware Widevine, such as many emulators, some AOSP builds like GrapheneOS, and some low-end hardware.
Collisions are possible, because devices of the same manufacturer or model can share a value.
Can still be spoofed on rooted devices using LSPosed or kernel-level modules that randomize it per profile.
MediaDRM is not unique enough on its own.
Identifiers built on it usually combine the MediaDRM ID with the device model. The device model absorbs collision risk and the DRM value provides persistence.
App Set ID
Identifies a group of apps that belong to the same developer account and is read through the Google Play services App Set ID API without a prompt. Unlike the Advertising ID, the user cannot reset it manually, but it is not permanent either.
Persistance
Survives reboots, OS updates, and app reinstall while at least one app in the set remains installed.
Reset by factory reset.
Reset when all apps in the set are uninstalled.
Reset after about 13 months without use.
Not resettable by the user.
Advantages
No permissions required.
Built for analytics and fraud prevention, according to Google.
Disadvantages
Cannot be used for ad personalisation or linked to the Advertising ID.
Plain string, so it can be spoofed on a rooted or hooked device.
Available only on Android devices with Google Play services.
IMEI / MEID / Serial number
These are identifiers stamped onto the physical device at the factory. The IMEI (usually 15 digits, viewable by dialing *#06#) identifies the phone's cellular hardware to the mobile network, and is separate from the SIM, which identifies the subscriber. The MEID is the older equivalent from CDMA networks and is mostly legacy today. The serial number is the manufacturer's label for the unit and has nothing to do with cellular, so a Wi-Fi-only device has one but no IMEI. All three are globally unique and burned into the hardware, so they survive reinstalls and factory resets. That permanence is exactly why access to them is now heavily restricted.
Persistance
Permanent, because the value is burned into the hardware or modem.
Survives factory reset.
Not resettable by the user.
Advantages
Device-wide and non-resettable
Disadvantages
Restricted to privileged apps since Android 10, so normal apps cannot read them.
The value is meaningful and counts as sensitive data, which conflicts with the privacy attribute.
IMEI and MEID exist only on devices with a cellular modem.
Historically used, but practically dead for normal app use today.
MAC Address
The MAC address is the hardware address of the device network interface. It was once a convenient device-wide identifier, but both the platform and the hardware vendors now hide the real value.
Persistance
The real hardware address is permanent, but apps can no longer read it.
Since Android 10 the device uses a randomised MAC per network by default.
The randomised value changes per network, so it is not stable.
Advantages
None that are usable in practice today.
Disadvantages
Apps read a randomised or placeholder value, not the real address.
The factory address is hidden behind a privileged permission.
Randomisation changes the value per network, so it cannot be used for identification.
Like IMEI, the MAC address is effectively unavailable for device identification today.
Hardware-backed Keystore + Key Attestation
This is not a value you read, but a key you create. You generate a key pair inside the Android Keystore, backed by the TEE (Trusted Execution Environment) or by StrongBox, a separate tamper-resistant chip. The private key cannot leave secure hardware. Key Attestation then produces a certificate chain that your server can verify to confirm the key was generated in real hardware. Instead of trusting a value the client reports, your server verifies a signature the client cannot forge.
Persistance
The key survives reboots and OS updates.
The key is tied to the app and is lost on uninstall unless you design around it.
Not a device-wide value, so it does not identify a device across apps on its own.
Advantages
The private key cannot be copied off the device.
Your server verifies it cryptographically, so it does not rely on a reported string.
Strongly resistant to spoofing.
No permissions required.
Disadvantages
More work to implement than reading a value.
Attestation coverage and quality vary between manufacturers.
StrongBox is not available on every device.
The key is lost on uninstall, so it is not a persistent device identifier on its own.
This is an integrity mechanism, not a stored identifier.
Use it to prove a request comes from genuine hardware, not to recognise a device across installs.
Play Integrity API
Play Integrity is not an identifier. It is a signal from Google that tells your server whether the app is genuine and unmodified, whether the device is genuine, and whether the app came from an official source. You request a token on the device and verify it on your server. It answers whether you can trust the client, not which device it is.
Persistance
Not applicable, because it returns a per-request verdict rather than a stored value.
Advantages
Strong, server-verified signal of app and device genuineness.
Hardware-backed on modern devices, so it is hard to spoof.
Designed for abuse and fraud protection.
Disadvantages
Cannot be used as an identifier, because there is no stable value to store.
Depends on Google Play services and is subject to rate limits and outages.
Can produce failures on custom setups such as GrapheneOS or rooted devices, which can lock out genuine users.
Google recommends against using it as the only allow or deny decision.
Treat this as one signal among several. It tells you whether to trust a device, not how to identify one, and it should not be your only gate.
Google Services Framework ID (GSF ID)
The GSF ID is a device-wide value created when the device registers with the Google Services Framework, the component behind Google Play services. It is read by querying an internal content provider (content://com.google.android.gsf.gservices, key android_id) and returns the same value to every app on the device. It is not an official or documented identifier API, and Google does not recommend relying on it.
Persistence
Survives reboots, OS updates, and app reinstall.
Reset by factory reset.
Reset when Google Services Framework data is cleared or the device re-registers with Google.
Not resettable by the user through a normal setting.
Advantages
Device-wide, so it is shared across apps and user profiles.
No dedicated permission for the query in practice.
Disadvantages
Not an official or documented API, so it can change or break without notice.
Available only on devices with Google services, so it is missing on non-GMS devices.
Tied to the Google registration and shared across apps, which is a privacy concern.
Plain value read from a provider, so it can be spoofed on a rooted or hooked device.
This is an unofficial identifier read from an internal provider. Prefer the App Set ID or a value you generate yourself.
Firebase Installation ID (FID)
The Firebase Installation ID identifies one installation of your app on one device. It is provided by the Firebase Installations SDK, read via FirebaseInstallations.getInstance().getId(), and is used internally by Firebase services such as Cloud Messaging, Analytics, and Remote Config. It is available on both Android and iOS when you use Firebase.
Persistence
Survives reboots and OS updates.
Lost on app uninstall, because it is tied to the installation.
A new value is created on reinstall.
Can be deleted and regenerated through the API.
Advantages
Simple to use if you already use Firebase.
Cross-platform, available on Android and iOS.
Per-install scope, which is privacy-reasonable.
Disadvantages
Identifies an installation, not a device, so a reinstall looks like a new identity.
Not suitable for device blocklisting.
Requires the Firebase SDK.
Use this to identify an app installation for Firebase features, not to recognise a device across reinstalls.
Instance ID (IID)
Instance ID was the older Firebase and Google Cloud Messaging identifier that provided a unique ID per app instance together with messaging tokens. It has been deprecated and split into two replacements: the Firebase Installation ID for the identifier, and Cloud Messaging registration tokens for messaging. It is listed here mainly so you recognise it in older code.
Persistence
Behaved like the Firebase Installation ID: tied to the app installation and lost on uninstall.
Advantages
None today, because it is deprecated.
Disadvantages
Deprecated and replaced by Firebase Installations and Cloud Messaging tokens.
Should not be used in new code.
Deprecated. Use the Firebase Installation ID and Cloud Messaging tokens instead.
iOS
Apple does not provide a stable device-wide identifier to third-party apps by design. The options below are either scoped to your own apps, resettable, or integrity mechanisms rather than identifiers.
identifierForVendor (IDFV)
The identifier for vendor, or IDFV, is a UUID that iOS assigns to all apps from the same vendor on a device. Vendor is determined by the first parts of the bundle identifier, for example com.example. It is read via UIDevice.current.identifierForVendor. It is the standard no-permission identifier for first-party analytics across your own apps, and it is the closest iOS has to Android ID.
Persistance
Survives reboots and OS updates.
Stays the same while at least one app from the vendor is installed.
Reset when the user removes all of the vendor apps, so a single app gets a new value after reinstall.
Reset by factory reset.
Not directly resettable by the user.
Advantages
No permission and no tracking prompt required.
Available immediately when the app launches.
Shared across all of your own apps on the device.
Disadvantages
Resets when the user removes all of your apps.
Scoped to your vendor account, so it cannot match users across other publishers.
Plain value, so it can be spoofed on a jailbroken device.
UDID and MAC address
The UDID was the original device-wide identifier on iOS, and the MAC address was a common substitute after it was removed. Both are now closed to third-party apps and are listed here mainly so you recognise them in older code.
Persistance
Both were permanent when they were available, but neither is accessible to apps today.
Advantages
None today
Disadvantages
The UDID has not been available to third-party apps for many years.
The MAC address returns a fixed placeholder value since iOS 7.
Neither can be used for device identification.
Do not design around these. They are no longer available to apps.
Keychain-stored UUID
When the platform identifiers do not fit, a common pattern is to generate your own UUID and store it in the Keychain. The Keychain is not cleared when the app is deleted, unlike UserDefaults, so the value can survive reinstalls. This gives you an identifier you fully control.
Persistance
Survives reboots, OS updates, and app reinstall in practice, because the Keychain is not cleared on delete.
Reset by factory reset.
Not resettable by the user unless you provide a way.
Advantages
Fully under your control.
Survives app reinstall in practice.
No permissions required.
Disadvantages
Survival across reinstall is not guaranteed by Apple.
Scoped to your app, not the whole device.
Client-side value, so it can be spoofed on a jailbroken device.
Apple has stated that Keychain persistence after app deletion was never a documented contract, only an implementation detail that could change.
Build on it, but design for the day it stops behaving.
DeviceCheck
DeviceCheck is not an identifier. It gives you two bits of storage per device that Apple keeps on its own servers, and those bits survive app deletion and reinstall. A common use is to record whether a device has already used a one-time offer, such as a free trial. Your server reads and writes the bits through Apple, and the token the device produces via DCDevice is different on every call.
Persistance
The two bits survive reboots, app reinstall, and app deletion, because Apple stores them server-side.
The token itself is not persistent, because it changes on every call.
Not resettable by the user.
Advantages
Server-verified state that survives reinstall.
Cannot be cleared by reinstalling the app.
No permissions required.
Disadvantages
Only two bits of state, not an identifier.
Cannot uniquely identify a device.
Requires your server to call Apple to read or write the bits.
Use this to remember a small fact about a device, not to identify it.
App Attest
App Attest is the iOS equivalent of Android Key Attestation. It uses a hardware-backed key in the Secure Enclave together with attestation to prove that a genuine copy of your app is running on a genuine Apple device. Like Play Integrity, it answers whether you can trust the client rather than which device it is.
Persistance
The attestation key lives in the Secure Enclave and is tied to the app.
Not a stable device identifier, because it is an integrity mechanism.
Advantages
Hardware-backed and verified on your server.
Strongly resistant to spoofing.
The right tool when the question is whether to trust the client.
Disadvantages
Not an identifier, so there is no stable value to store.
More work to implement, and it needs a network call to attest.
Available on iOS 14 and later.
Like Key Attestation on Android, this proves trust in a client. It does not identify a device across installs.
Further reading
Last updated
Was this helpful?

