What do App Attest and DeviceCheck prove, when do they help, and what are their limits?

14.2.2 · L5 · Interview

Predict first

In the simulator, does the device support App Attest or DeviceCheck?

import DeviceCheck

print(DCAppAttestService.shared.isSupported, DCDevice.current.isSupported)
  • false false
  • true true
  • true false

Mental model. App Attest is a tamper-evident seal issued by the manufacturer: it proves the package left the factory untouched. It does not tell you who is holding the package or what they intend to do with it.

App Attest (the DeviceCheck framework’s DCAppAttestService) lets your server ask: “is this request coming from a genuine, unmodified instance of my app on a real Apple device?” The flow: the app generates a key pair in the Secure Enclave (generateKey), asks Apple to attest it with your server’s challenge (attestKey(_:clientDataHash:)), and sends the attestation object to your server, which verifies Apple’s certificate chain, the app identity (team and bundle id), the environment (development or production) and the challenge, and then stores the public key. After that, each sensitive request can carry an assertion (generateAssertion), a signature by that hardware key over the request data, which the server verifies against the stored key and a counter (to stop replays). Because the key lives in the Secure Enclave and is bound to your app, a bot, a modified binary, a simulator or a script calling your API cannot produce valid assertions. It is unavailable in the simulator and on unsupported devices (isSupported is false), so you need a fallback path.

When it helps: protecting expensive or abusable endpoints (sign-up, free-trial grants, in-app reward claims, scraping, credential stuffing, high-value purchases) against fake clients, and as one risk signal in fraud scoring. When it does not help: it does not prove the user is who they claim (use authentication), it does not stop a real, genuine app on a real device from being driven by an attacker with physical access, and it is not secrecy for data on the device. DeviceCheck (DCDevice) is simpler: it gives your server a token that lets it query and set two bits per device in Apple’s storage (for example “this device already claimed the promotion”), which survive reinstall without identifying the user. Design rules: verify server-side (client checks can be patched out), do not hard-fail on a missing attestation (degrade to extra checks or rate limits), attest once and then assert, watch the rate limits and the fraud-risk metric that attestation returns, and handle key invalidation (reinstall, restore to a new device). Combine with TLS, authentication and server-side authorisation; attestation is a layer, not a login.

The simulator has no Secure Enclave and cannot attest, so a client must handle the unsupported case.

Common traps

  • Verifying attestation on the client. A patched app skips the check; all verification belongs on the server.
  • Rejecting every request that lacks attestation. The simulator and some devices are unsupported; degrade gracefully.

They might follow up with

  • What does an assertion add after the first attestation? A per-request signature from the attested key, with a counter against replay.
  • When is DeviceCheck enough? When you only need a per-device flag, such as “promotion already used”.

Related questions

  • 14.2.1 How do Face ID and Touch ID with LocalAuthentication work, and what does the Secure Enclave add?
  • 14.5.1 How would you threat-model a consumer app that handles accounts, payments and personal data?
  • 12.2.2 How do TLS and App Transport Security protect a request, and what is certificate pinning, and why is it…

← How do Face ID and Touch ID with LocalAuthentication work, and what…