3
Core Play Integrity verdict fields: app recognition, device integrity, and account licensing
3
Device integrity tiers Play Integrity can return, from MEETS_BASIC_INTEGRITY to MEETS_STRONG_INTEGRITY
1
App Attest key generated per install; every later request reuses it for a lightweight assertion
2
Play Integrity request types: standard (low latency) and classic (higher latency, nonce-based)

Reach 50,000+ security professionals

Sponsor a newsletter slot and put your brand in front of CISOs, engineers, and IT decision-makers.

Summarize with:

A server that only checks an API key or a valid session token cannot tell the difference between your real app and a script that extracted the same key from your app's binary and replays your app's exact requests. Fake account creation, credential-stuffing bots wearing your app's own API contract, emulator farms running modified APKs for ad fraud or account takeover, and repackaged IPAs with tracking or license checks stripped out all look identical to a normal client at the network layer. Mobile app attestation closes that gap by asking the platform itself, Google Play services on Android or Apple's Secure Enclave and DeviceCheck framework on iOS, to vouch that the code making a specific request is your unmodified app, running on a device the platform considers genuine. This guide covers the actual implementation: client-side token and assertion requests, server-side verification against each vendor's public trust anchor, and the parts teams most often get wrong, including treating a verdict as a log line instead of an authorization input. For the testing side of mobile app security that attestation does not cover, see our mobile application security testing guide.

At a glance: Play Integrity API vs. App Attest

Google Play Integrity APIApple App Attest
PlatformAndroidiOS 14 and later
Underlying serviceGoogle Play servicesApple DeviceCheck framework, backed by the Secure Enclave
What it verifiesApp recognized by Play, device integrity tier, account licensingA hardware-backed key pair generated on a genuine device, tied to your specific app
Request patternRequest a token per sensitive action, bound to a nonce or request hashGenerate one attestation per installed key, then a lightweight assertion per request
Server verificationDecode and verify the token's signature, then read the verdict fieldsVerify the attestation chain to Apple's App Attest root CA once, then verify each assertion's signature and counter
Replay protectionNonce or request-hash binding; verdicts are not meant to be cachedA per-assertion counter that must strictly increase

The problem: why a server cannot trust a raw API call

TLS proves a connection is encrypted; it says nothing about what wrote the request on the other end. An API key or client secret embedded in an app binary can be extracted with standard reverse-engineering tools and replayed from a script that never touches your actual app. A tampered APK can strip out client-side checks, spoof telemetry, or automate flows a human would normally have to click through manually. None of that shows up as malformed traffic, because the requests are syntactically identical to what your real app sends. Server-side rate limiting and bot-detection heuristics catch volume and pattern anomalies, but they cannot definitively answer the narrower question attestation is built for: is this specific request coming from an unmodified copy of your app, running on a device the OS vendor itself considers real and uncompromised. Play Integrity and App Attest answer that question using signing keys and hardware roots of trust that live outside the app binary an attacker can extract and inspect, which is what makes the verdict harder to forge than anything client-side code alone could produce.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Prerequisites

For Android (Play Integrity):

  • Your app published on Google Play, at minimum on an internal or closed testing track; Google's standard and classic request types both expect a Play-associated app.
  • A Google Cloud project linked to your Play Console app, with the Play Integrity API enabled on that project.
  • A backend credential (service account or OAuth client) your server can use to call Google's token decoding and verification endpoint.

For iOS (App Attest):

  • An active Apple Developer Program membership.
  • The App Attest entitlement added to your app's entitlements file, with the environment set to development or production as appropriate for the build.
  • iOS 14 or later as your deployment target, and physical devices for testing; App Attest is unavailable on the iOS Simulator.
  • Apple's App Attest root CA certificate available to your server for certificate chain verification.

On the server, for both platforms:

  • A datastore to persist per-device state. Play Integrity itself is stateless from your server's perspective beyond your own risk decisions, but App Attest requires you to store each device's public key and last-seen counter value indefinitely for that device.
  • A written policy, agreed before implementation, for exactly what your backend does with each verdict outcome. Writing this after the first fraud incident means making the decision under pressure instead of in advance.

Step by step: requesting an integrity token on Android

  1. Choose a request type per use case. Use a standard request (a few hundred milliseconds of added latency) for frequent checks like every login or checkout, and reserve a classic request (multi-second latency) for infrequent, high-value actions like account creation or a large transaction.

  2. Generate the binding value on your server and send it to the client for that specific request: a request hash for standard requests, built from the meaningful contents of that request such as the account ID and transaction amount, or a nonce for classic requests. Generate a new value per request, not once per session; a value that gets reused defeats the anti-replay purpose entirely.

  3. On the client, call the Play Integrity API with that request hash or nonce and receive an encrypted, signed integrity token in response.

  4. Send the raw token to your backend over your existing authenticated channel. Do not attempt to decode or branch on verdict fields inside the app. Treat the token as opaque until it has been verified server-side; any client-side integrity check is something an attacker with a modified binary can simply patch out.

Step by step: generating keys and attestations on iOS

  1. On first use of a feature that needs attestation, call DCAppAttestService.generateKey to create a hardware-backed key pair inside the Secure Enclave, and persist the returned key identifier locally on the device.

  2. Request a challenge from your server. It must be unique and unpredictable for each attestation; reusing a challenge undermines the freshness guarantee the whole flow depends on.

  3. Call attestKey with that key identifier and a hash of the challenge as the client data hash, and receive a CBOR-encoded attestation object back.

  4. Send the attestation object, the key identifier, and the challenge context to your server for one-time verification. Once a key has been verified, do not re-attest it; App Attest is designed for one attestation per key, followed by many lightweight assertions.

  5. For every subsequent sensitive request, call generateAssertion with the same key identifier and a hash of that specific request's payload as the client data hash, and send the resulting assertion alongside the request.

Step by step: server-side verification and tying results to authorization

For Play Integrity tokens: first, decode and verify the token using Google's server-side decoding call, which returns a JSON payload containing requestDetails and the verdict fields. Second, confirm requestDetails.requestPackageName matches your app's package name and requestDetails.timestampMillis is recent; Google's own guidance is to treat an old token as suspicious rather than reuse it. Third, confirm the request hash or nonce embedded in the token matches the value you generated for this exact request; a mismatch means the token was not bound to what the client is actually asking for. Fourth, read appRecognitionVerdict, deviceIntegrity, and appLicensingVerdict and apply your policy. Log every verdict, including passing ones, so you have real data to tune thresholds against later instead of guessing.

For App Attest: on the initial attestation, verify the certificate chain up through Apple's App Attest root CA, extract the credential certificate, and confirm its embedded nonce extension matches the SHA-256 hash of your challenge combined with the attestation's authenticator data. Extract the public key from that certificate and confirm its SHA-256 hash matches the key identifier the client sent. Verify the initial counter value is zero, then persist the public key and counter for that device's key identifier. On every assertion after that, verify the signature over the client data hash and authenticator data using the stored public key, and confirm the counter strictly increased from the value you have on record; if it has not, reject the request as a likely replay.

The most common implementation mistake is treating either verdict as something to log for later analysis rather than something that changes what the request is allowed to do right now. State the policy plainly: a failed or missing attestation on a sensitive action should route the request to a stricter path, such as a CAPTCHA challenge, delayed account activation, or manual review, not sail through unchanged with a note in a log. Reserve outright blocking for cases where you have already built the fallback described below, since both attestation services do have outages.

Validation: testing rooted devices, emulators, and replay protection

Run your Android build on a rooted device (a common Magisk setup) and on a widely used emulator, and confirm deviceIntegrity comes back below the tier you require, or is missing entirely. Repackage your own APK with a trivial code change, resign it, and install it, then confirm appRecognitionVerdict is no longer PLAY_RECOGNIZED. Capture a valid integrity token or App Attest assertion and manually replay it against your backend with a different request hash, nonce, or an old counter value, and confirm your server rejects it outright rather than accepting it. Test on at least three genuine, unmodified consumer devices per platform, not only your own development hardware, so you have a real baseline for what a passing verdict looks like before you turn on enforcement. For iOS specifically, run this validation on real hardware; App Attest returns an error on the Simulator, so simulator-only testing cannot validate the flow end to end.

Failure cases: outages, false positives, and stale verdicts

Attestation service outages: both Google's and Apple's services can become unreachable for reasons entirely outside your control. Decide your fallback (fail open with heavier server-side risk scoring and logging, or fail closed and accept that some legitimate users get blocked) before you ship, not during an incident. Google's own integration guidance recommends against a hard binary allow or deny outcome for exactly this reason, suggesting a range of responses such as allow, allow with limits, or allow after a CAPTCHA, so a missing or degraded verdict does not have to mean an outright block.

False positives on legitimate but unusual devices: heavily customized Android ROMs, rooted developer phones, and devices under corporate mobile device management can return a lower device integrity tier despite being used by legitimate people rather than fraud actors. Our MDM and BYOD security guide covers the device-management side of this overlap in more detail. Treat a tier like MEETS_BASIC_INTEGRITY as a reason to add friction, not as grounds for an automatic block, and build a step-up or appeal path for that segment of users.

Verdict caching and staleness: Google's documentation is explicit that Play Integrity verdicts should not be cached, because caching opens a proxying window where an attacker captures one genuine device's good verdict and replays it from a different, non-genuine environment for as long as the cache is honored. Request a fresh token close to the specific action being protected, not once per app session.

Counter drift on iOS: if your server ever loses the stored counter value for a device, through a backup restore or a database issue on your side, you either have to force re-attestation for that device or risk rejecting legitimate assertions that no longer match your stale record. Build a defined re-attestation path for this case rather than only a hard failure.

Security tradeoffs: what attestation does and does not solve

Attestation raises the cost of automated abuse; it does not eliminate a resourced attacker. Someone running real, unmodified copies of your app on real devices, whether through device farms or paid human labor clicking through your actual UI, will attest as genuine because the traffic genuinely is coming from your real app on real hardware. Attestation is built to defeat server-side scripts and tampered clients, not abuse that runs entirely through your legitimate app.

Privacy considerations follow directly from what these systems persist. App Attest's hardware-backed key and counter are a persistent, device-tied identifier by design, and Play Integrity's device signals are adjacent to device fingerprinting even though Google does not market them that way. Treat the state your server persists for these checks as sensitive, account-linked data in your own retention and access policy, and disclose its use wherever your privacy policy already covers device or fraud-prevention signals.

Neither API replaces server-side abuse detection; both are meant to sit alongside it. Attestation answers whether a specific request came from your real app on a real device, not whether the account or session behind that request is behaving normally. Pair a passing verdict with the behavioral and velocity-based detection covered in our mobile threat defense guide rather than treating attestation on its own as sufficient fraud coverage.

The bottom line

Play Integrity API and App Attest solve a narrow but real problem: proving a specific request came from your unmodified app on a genuine device, using trust anchors an attacker cannot extract from the client binary. Implementing them correctly means binding every token or assertion to the specific request it protects, verifying signatures and certificate chains server-side against each vendor's own root of trust, and routing failed or missing verdicts into an actual authorization decision rather than a log entry. Neither service is complete on its own. Plan for outages, false positives on legitimate but unusual devices, and the fact that a well-resourced attacker running your real app on real hardware will still pass attestation, which is why these checks belong alongside server-side behavioral abuse detection, not in place of it.

Frequently asked questions

What is the difference between Google Play Integrity API and Apple App Attest?

Play Integrity API is Google's Android attestation service, returning a signed verdict on app recognition, device integrity, and account licensing for a specific request. App Attest is Apple's iOS equivalent, using a hardware-backed key pair in the Secure Enclave to produce a one-time attestation followed by per-request assertions, verified against Apple's own root certificate.

Does Play Integrity or App Attest stop rooted devices and emulators entirely?

They flag rather than block network access outright. A rooted device or emulator typically returns a lower device integrity tier, below MEETS_DEVICE_INTEGRITY, or an App Attest failure, and it is your backend's own policy, not the API itself, that decides whether to add friction or deny the request.

Can attestation verdicts be cached to reduce API calls?

Google explicitly advises against caching Play Integrity verdicts, since a cached genuine verdict can be captured and replayed from a non-genuine environment for as long as the cache is honored. Request a fresh token or assertion close to the specific action you are protecting.

What happens if the Play Integrity or App Attest service has an outage?

Both services can become unreachable independent of your own app's health. Define a fallback policy in advance, typically routing to stricter server-side checks like a CAPTCHA or manual review rather than a hard block, since deciding this mid-incident risks either locking out legitimate users or disabling the protection entirely.

Will legitimate users on rooted phones or corporate MDM devices get blocked by attestation?

They can receive a lower integrity verdict without being fraudulent, particularly on heavily customized ROMs or devices enrolled in corporate mobile device management. Treat a lower tier as a signal to apply step-up verification rather than an automatic denial, and build an appeal path for that segment.

Is mobile app attestation enough on its own to stop fraud and bot abuse?

No. Attestation proves a request came from your genuine, untampered app on a real device, but it cannot detect abuse carried out through your real app on real hardware, such as device farms or manual click-farm labor. It needs to work alongside server-side behavioral abuse detection, not replace it.

Sources & references

  1. Google Play Integrity API overview
  2. Play Integrity API verdicts reference
  3. Play Integrity Classic API nonce guidance
  4. Apple: Establishing your app's integrity (App Attest)
  5. Apple DeviceCheck framework documentation
  6. Apple certificate authority (App Attest root CA)
Summarize with:

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Follow Decryption Digest

Daily briefings + Google Top Stories in one step.

Enter your email and we'll subscribe you to the daily briefing and add Decryption Digest to your Google Search feed at the same time.

Free. Unsubscribe anytime. Google popup confirms the Top Stories add.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Daily Briefing

Get briefings like this every morning

Actionable threat intelligence for working practitioners. Free. No spam. Trusted by 50,000+ SOC analysts, CISOs, and security engineers.

Unsubscribe anytime.

Mythos Brief

Anthropic's AI finds zero-days your scanners miss.