What App Attest Verifies and What It Does Not

The boundary verified by App Attest, short-lived tokens, and request proofs—and the attacks left outside it

Hangangjari provides parking, events, weather, and notifications without user accounts. In place of user authentication, the server needs a signal that an HTTP request started from a registered iOS app instance. API addresses and payload formats can be observed, and an API key embedded in the app can be extracted.

Hangangjari registers app-instance keys with App Attest. Assertions are used only for requests that can change state or operational metrics.

General requests carry an app-instance signal. Requests that change server state or operational metrics add body-integrity verification.

Attack Surface Remaining After App Attest

After App Attest is applied, attackers can still observe public APIs and build automated callers. The server uses the following signals in risk assessment and request-authorization policy.

  • Verify that the request started from a distributed app instance.
  • Verify continuity with the key and verification material registered by the server.
  • Verify the core content of state-changing requests.
  • Grant fallback signals only limited authority.

The first-screen API can still be called repeatedly from outside the app. The server distinguishes calls without an app token from registered app requests and applies rate limits and abuse metrics. Requests such as notification-setting changes create server state, so they verify both the app-instance signal and the request body.

App Instance Registration

A security module in the iOS app checks App Attest availability and, when supported, creates a key on the device.

The app does not receive the private key directly. It receives only a key identifier, and stores it in secure device storage only after registration succeeds.

The registration flow starts with a server challenge.

sequenceDiagram
  autonumber
  participant App as iOS app
  participant Apple as Apple App Attest
  participant API as Hangangjari API
  participant DB as App-instance store

  App->>API: Request registration challenge
  API-->>App: One-time challenge
  App->>Apple: Create key attestation
  Apple-->>App: Attestation object
  App->>API: Register key ID and attestation object
  API->>API: Verify attestation
  API->>DB: Store app-instance verification material
  API-->>App: Short-lived app access token

The server verifies the Apple App Attestation certificate chain, server-issued challenge, app identity context such as the App ID prefix and Bundle ID, and key identifier, then stores the public key and counter used for later assertions.

Short-Lived Tokens for Ordinary Requests

After registration, the iOS app does not repeat attestation for every request. It uses the registered key to refresh a short-lived app access token and includes that token in a header on general API requests.

flowchart LR
  Request["General app API request"] --> Header["App access token header"]
  Header --> Verify["Verify signature and expiry"]
  Verify --> Instance["Look up app instance"]
  Instance --> Context["Check registration context and state"]
  Context --> API["Handle API request"]

This is not a user authentication token. The server checks the registered app instance and token state before authorizing general API requests.

After verifying the token, the server checks the app instance in DB again. If the token’s app instance does not match the stored registration context, or if the instance is inactive, the request is rejected.

Binding the Body of State-Changing Requests

Write requests that create state or can pollute operational metrics carry request proof signed with the registered key.

Request categoryWhy it needs stronger handling
Notification-setting changesThey create or clean up notification settings on the server.
Telemetry collectionThey affect events and performance signals used as operational metrics.

The token verifies a registered app instance and expiry. Request proof verifies an assertion over the server challenge and request-body summary.

The server and app combine a one-time challenge with a summary of the request body to create the proof challenge. The app signs it as an App Attest assertion, and the server verifies it with the stored public key. A modified body no longer matches the assertion’s client data. The assertion counter must also exceed the previously stored value, causing captured request replays to be rejected.

Restricted Authority for Fallback

App Attest is not available in every iOS execution environment. Development environments, some runtime environments, and extension contexts can differ.

The Hangangjari iOS app separates development policy from distribution-build policy.

During development, local checks need a way not to block iteration. In distribution builds, App Attest failure does not silently pass.

DeviceCheck fallback also exists. If App Attest is impossible and DeviceCheck is available, the app can receive an app access token with a limited fallback signal.

The server stores fallback tokens with different authority from App Attest tokens. They allow general reads but cannot be used for requests that require request proof.

flowchart TD
  Signal["App-origin signal"] --> Strong{"App Attest available?"}
  Strong -->|"Yes"| AppAttest["General requests + strong request verification"]
  Strong -->|"No"| Fallback["Limited fallback"]
  AppAttest --> Allow["Allow according to request type"]
  Fallback --> Limited["Allow only limited general app requests"]

Protection by Request Type

Hangangjari records what request verification changes after App Attest and which protections remain necessary.

SituationWhat changes after App AttestStill needed
Calls that mimic API shape outside the appWithout an app-origin signal, they are not treated as registered app requests.IP rate limits, abuse metrics, cache protection
Attempt to modify state-changing bodyThe core request content is verified together.Notification-setting protection, subscription cleanup, audit
Replay of an old requestOne-time challenges and a counter greater than the stored value are verified.Clock handling, storage incidents, retry policy
Environment without App AttestFallback is treated as limited authority.Fail-closed policy for distribution builds, development-token management
Compromised device or broker-style callsFraud metrics provide an additional risk signal.Rate limits and abuse monitoring

Apple states that App Attest alone cannot definitively identify a compromised operating system. Hangangjari uses the result as an app-origin risk signal alongside rate limits, log masking, token expiry, key recovery, and operational metrics.

App Attest Fraud Metrics

Apple lets the server send App Attest receipts back to Apple and receive a fraud metric: an approximate count of attested keys created for a device/app pair in the last 30 days.

Hangangjari classifies this as a metric for baselines and abrupt changes rather than an immediate blocking condition.

Reinstallation, backup restoration, and a change in device ownership can also increase the key count, so this metric alone does not block a request.

Verification Policy by Request Category

Each request policy states whether app-instance verification is required, whether the body needs an assertion, which fallback authority is allowed, and what happens after verification failure. General API requests use short-lived tokens, while requests that change state or operational metrics add request proof. DeviceCheck fallback is limited to general reads, giving each of the three request categories different authority.

The Apple documents I referenced were DeviceCheck, DCAppAttestService, Validating apps that connect to your server, and Assessing fraud risk.

Comments

Comments

    Image preview