A password or session token tells your backend who the customer claims to be. It says nothing about what is making the request. A cloned app, a modified build with the checks stripped out, or a script replaying captured API calls can all present a valid token. For a money transfer business, where the API can create recipients, move money and change account details, the backend needs a second answer: is this request coming from our genuine app, on a real device, right now?
This guide is for CTOs and security and compliance leads. It covers what platform attestation proves and what it doesn't, how the backend should verify and act on the result, and how to layer sign-in, device control, rate limits and logging around it so that one weak signal never decides the outcome on its own.
What your backend cannot tell from a password or token
Most mobile backends authenticate the user and trust everything else. Three kinds of client exploit that:
- Cloned apps: a lookalike app, distributed outside the stores or through phishing links, that captures credentials or one-time codes and replays them against your real API.
- Modified apps: your own app, decompiled and repackaged with checks removed, limits bypassed or traffic logged.
- Scripted clients: no app at all, just code calling your endpoints directly, often for account creation at scale, credential stuffing or probing limits.
Two common defences do not close this gap. Code signing and store review prove the app was genuine when it was installed; they say nothing about what is calling your API later. And an API key embedded in the app is a secret only until someone decompiles it, which for an Android build takes minutes. Obfuscation slows that down; it does not stop it.
Key distinction: user authentication answers "who is this?". App attestation answers "what is this?". A money transfer backend needs both on every sensitive request, and neither substitutes for the other.
What App Attest and Play Integrity prove, and what they don't
Both platforms let your app ask the operating system to vouch for it. The answer is signed by Apple or Google, so the app cannot forge it, and your backend checks it rather than trusting the app's word.
Apple App Attest (iOS)
The app generates a key pair held in the device's Secure Enclave, then asks Apple to attest that key against a challenge from your server. Your backend validates the attestation: the certificate chain leads to Apple's App Attest root, the challenge matches, and the embedded App ID (your team ID and bundle ID) is yours. After that, the app signs individual requests with the key ("assertions"), and your backend checks each signature and an incrementing counter.
What it proves: the request was signed by a key held on genuine Apple hardware, generated by your app as Apple identifies it. What it does not prove: that the device is not jailbroken. App Attest is not a jailbreak detector. Apple also offers a risk metric through the attestation receipt, which helps spot one device generating many keys.
Google Play Integrity (Android)
The app requests an integrity token, bound to a server challenge or a hash of the request. Your backend decodes it through Google's server API and reads a verdict with several parts:
- App integrity: whether Play recognises this exact package, signing certificate and version.
- Device integrity: whether the device passes basic, device or strong integrity checks. Rooted devices, emulators and devices with an open bootloader typically fail the stronger levels.
- Account details: whether the app was installed or licensed through Play.
- Request details: the package name, your challenge and a timestamp, so you can reject replays.
What neither proves
Attestation does not prove the person is the account holder, that the phone is free of malware using screen-reading or accessibility abuse, or that the customer is not being coached by a scammer while using your genuine app. It is a strong signal about the client, not a verdict on the transaction.
How to verify the result on the backend
Most attestation weaknesses come from how the result is checked, not from the platforms.
- Issue the challenge from the server. A random, single-use value with a short expiry. Never let the app choose its own.
- Verify on the server, never in the app. A check performed in the app can be patched out of a modified build. The backend decides.
- Bind the result to something. For Play Integrity, include a hash of the sensitive request so a token cannot be lifted and reused for a different action. For App Attest, sign the request body in the assertion and enforce the counter.
- Check every field you depend on. Package name or App ID, signing certificate digest, environment (production, not development), timestamp freshness. A valid signature over the wrong app is still the wrong app.
- Tie attestation to the session and device record. Attest at registration and sign-in, then on high-risk actions such as adding a recipient, changing contact details or confirming a transfer. Attesting every request spends platform quota for little gain.
Watch for: a backend that treats "no attestation token" as "skip the check". If the new release sends tokens but the old code path still accepts requests without one, attackers will simply use the old path.
How to decide what the backend does with each signal
A binary allow-or-block rule causes either fraud or support queues. Grade the response to the signal and the action being attempted.
| Signal | Backend action | Log and follow-up |
|---|---|---|
| Valid attestation, known device, normal behaviour | Allow | Record verdict summary against the session |
| No token, or token fails signature or challenge check | Block the request | Log as suspected scripted or cloned client; count by IP and account |
| App not recognised (wrong package, certificate or App ID) | Block; end the session | Treat as a modified or cloned app; review the account |
| Genuine app, weak device integrity (likely rooted or emulator) | Allow low-risk reads; require step-up or hold new recipients and transfers for review | Flag the device; feed into risk checks |
| Platform cannot evaluate (service unavailable, unsupported device) | Allow sign-in with stricter limits; retry attestation before sensitive actions | Log the reason so outages are distinguishable from attacks |
| Replayed token or stale timestamp | Block | Log as replay attempt; alert if repeated |
| Valid attestation, new device on an existing account | Require one-time code before PIN or biometric sign-in is enabled | Add to the customer's device list |
How to layer sign-in, devices and risk checks around attestation
One-time code, then PIN or biometrics
A practical pattern is a one-time code to bind a new device, then PIN, Face ID or fingerprint for everyday sign-in. The code proves control of the phone number or email at enrolment; the PIN or biometric releases a credential stored on that device. Combined with attestation, an attacker needs the customer's code, a genuine app and a device that passes checks, which is a much harder set to assemble than a password.
A device list the customer can act on
Every signed-in device should appear on a list the customer can see. Removing a lost phone must revoke its sessions and its PIN and biometric sign-in on the server, not just hide it from the list. This also gives your support team a clear, auditable action when a customer reports a stolen phone.
Rate limits and risk checks
Attestation stops clients that are not your app; it does not stop a fraudster using your genuine app. Keep rate limits on sign-in, one-time code requests and recipient creation, keyed on account, device and network. Feed device signals into transaction risk checks, so a transfer to a new recipient from a newly enrolled, weak-integrity device waits for a person rather than flowing automatically.
Staff access is a separate front door
The admin panel can approve payouts, change rates and override checks. Staff sign-in needs its own two-step sign-in and role-based permissions; customer-app controls do nothing for it.
What to log so the audit trail holds up
When a regulator, auditor or your own investigation asks why an account was blocked or a transfer allowed, the answer should come from records, not memory. For each attestation and sign-in event, record:
- Time, account, device identifier and app version.
- The verdict summary: pass, fail or not evaluated, and which part failed. Store the decision and the reason, not raw tokens.
- The action taken (allowed, stepped up, held, blocked) and the rule that caused it.
- Device list changes: added, removed, and by whom (customer or staff).
- Any staff override, with the person and the reason.
- Changes to the enforcement rules themselves: who changed what, when, and what it was before.
Apply the same retention and access rules as your other security logs.
What to check before you enforce: false positives, rooted phones and old devices
- Genuine customers on rooted or custom-ROM phones. Some are enthusiasts, not fraudsters. Decide in advance whether they get reduced functionality or a block, and write the support script before launch.
- Old operating system versions. App Attest needs iOS 14 or later and supported hardware; it is unavailable on simulators. Older Android devices may not reach stronger integrity levels, and devices without recent security updates can fall back. Check your customer base by OS version before choosing a threshold.
- Phones without Google Play services. Some Android devices sold in certain markets cannot return a Play Integrity verdict at all. If your corridors include those markets, plan a fallback path with stricter limits rather than a hard block.
- Platform outages and quota. Both services can be unavailable or rate-limited. Treat "could not evaluate" as its own state, never as a pass and never automatically as fraud.
- Sideloaded or test builds. Internal builds and testers' devices will fail production checks. Keep development environments separate rather than weakening production rules.
Scenario: a scripted client tries to open accounts
The numbers below are illustrative, chosen to show the reasoning rather than to describe any real operator.
Step 1: the pattern appears
On a Monday morning, the security lead sees 140 sign-up attempts in 20 minutes, all requesting one-time codes to different mobile numbers, all from a small range of networks. None carries an attestation token.
Step 2: the backend acts without anyone intervening
Because the backend requires attestation before it sends a one-time code, every request is rejected before a single message is sent. The rejections are logged as "no token", grouped by network. Rate limits would have caught them later; attestation stopped them at the first request.
Step 3: the attacker adapts
Later that day, 12 sign-ups arrive with valid tokens from the genuine Android app, but device integrity shows only the basic level, consistent with emulators. The decision table applies: accounts can be created, but adding a recipient or sending a first transfer is held for review.
Step 4: people decide the edge cases
The compliance team reviews the 12. Ten share device characteristics and stall at the ID check; they are closed. Two are genuine customers on older phones; their holds are released with the reason recorded.
Step 5: the rules are reviewed
The team leaves the thresholds unchanged. Every step, including the two releases, is in the audit trail.
How to roll out attestation without locking out customers
- Ship the client change first. Release app versions that request and send attestation, while the backend only records results.
- Run in monitor mode. For a few weeks, log verdicts without acting. Measure the share of genuine customers who fail, by platform, OS version and market.
- Enforce on the clearest cases. Block missing, forged and replayed tokens and unrecognised apps first. These have almost no legitimate explanation.
- Add graded responses. Introduce step-up and holds for weak device integrity once you know how many customers it affects.
- Set a minimum app version. Once most customers have updated, refuse old versions that do not send tokens, and close the old code path.
- Review monthly. Track block and hold rates, support contacts and fraud cases together, and adjust thresholds with the change recorded.
Takeaway: attestation is the gate, not the whole wall. Verify it on the server, grade your response, keep sign-in and device control strong behind it, and log every decision so you can explain it later.
Doing it with RemitSo
RemitSo's customer app and admin panel include the controls described above. Your team still sets risk rules, reviews held transfers and decides how to treat edge cases.
- Only genuine apps can connect: app integrity checks mean the backend accepts requests only from the genuine customer app. See the customer app features.
- Sign-in: a one-time code, then PIN, Face ID or fingerprint.
- Device list: every signed-in device on one list; removing a lost phone in one tap stops its sessions, PIN and Face ID sign-in.
- Risk checks: 46 ready-made risk indicators you combine into your own rules; risky transfers wait for a person.
- Staff access: two-step sign-in (authenticator app or email code) and roles built from permissions, so each person sees only what their role allows. See the admin features.
- Audit log: a searchable, plain-language audit log of who changed what, and when, plus every staff and customer sign-in with IP, location and whether MFA was completed.
- Rate limits: sign-in, sign-up, verification codes and password resets are rate-limited in the apps and the console.
- Certification and hosting: ISO 27001:2022 certified, with each operator hosted in a dedicated AWS account in its own region.
Read the release notes for recent changes, or book a demo to see the sign-in and device controls in practice.
Sources
- Apple Developer Documentation: DCAppAttestService (App Attest)
- Android Developers: Play Integrity API, Integrity verdicts
Checked October 2026. Platform requirements change; confirm current behaviour in the platform documentation.
Frequently asked questions
Does App Attest detect jailbroken iPhones?
Not directly. It proves a request was signed by a key on genuine Apple hardware, created by your app. Jailbreak detection needs separate signals, and behavioural risk checks matter more than any single detector.
Should we block every rooted Android device?
Usually not outright. Block unrecognised apps and forged tokens; for weak device integrity, allow low-risk actions and hold or step up new recipients and transfers. Measure the effect on genuine customers in monitor mode first.
Isn't an API key in the app enough to stop scripts?
No. Anything shipped inside the app can be extracted from it. Attestation works because the proof is signed by Apple or Google and bound to a challenge from your server, so it cannot be copied from the app.
What happens if Apple's or Google's service is down?
Treat "could not evaluate" as a separate state. Allow sign-in with stricter limits, retry before sensitive actions and log the reason, so an outage does not lock out customers or look like an attack.
Does attestation stop authorised push payment scams?
No. In those cases the genuine customer uses your genuine app. Clear payee information, scam warnings and risk checks that hold unusual transfers for review are the controls that help there.