Say a bank offers Rp50,000 cashback to every new customer who signs up through its app. Over one weekend, thousands of new accounts register and all of them claim it. In the server logs, the requests look normal: right endpoint, right API key, right data format. What the logs don't show is that most of those requests were never sent from the app.
It isn't hard to do. The attacker installs the app on their own phone and watches which requests it sends to the server and what's in them. API keys, client IDs and other headers are stored inside the app, so all of it can be copied. After that, a script can send the same requests thousands of times without opening the app.
The same approach is used to test leaked passwords (credential stuffing), claim vouchers over and over, and trigger large volumes of SMS OTPs that the bank pays for. OWASP lists this pattern among its API security risks, with an example of a script that registers accounts automatically to collect referral bonuses.1
Why common controls fall short
An API key built into the app only proves that the sender once had the app, and anyone can download it. Headers and user-agent strings get copied too. Anything the app reports about itself, such as "device not rooted", a script can report just as easily. Certificate pinning makes traffic harder to inspect, but not for someone who controls their own phone. IP rate limits can be avoided by switching networks.
These controls still help, because they add work for the attacker. The problem is that none of them proves a request actually came from your real app.
What each request needs to prove
The server needs evidence it can check itself. Google (Play Integrity and key attestation) and Apple (App Attest) provide the mechanism: the phone produces proof signed by its secure hardware, and the server checks that the proof comes from your real app on a real device.23 Emulators fail here, because they don't have that hardware.
Two more conditions matter. First, the proof has to be checked on the server. Apple's documentation explains why: an app can't be relied on to check its own security, because a modified app can fake the result.3 Second, the proof has to answer a unique challenge from the server and be valid only once, so a request the attacker recorded can't be sent again.
The limit: phone farms
Attestation stops scripts, recorded requests and emulators. Serious attackers then move to phone farms: dozens or hundreds of cheap real phones running the real app automatically. Here attestation passes, because the phones and the app are genuine.
What still shows is how the phones are used. One phone registers many accounts. One device switches between accounts. Taps are too consistent to be human. Many devices go through the same steps at the same time. None of this is in the transaction log; it has to be collected from the device.
Questions for your team
- With every key and header from your app, could someone register an account without opening it?
- Does your server check attestation itself, or accept the result from the app?
- Could a request recorded yesterday still be used today?
- Would you know if ten accounts were registered from one phone in an afternoon?
Where MoSecure fits. MoNetra checks on its server that a session comes from the real app on a real device, then gives the bank's backend a short-lived, single-use token for each sensitive action. It also detects patterns across accounts, such as several registrations from one phone. MoTogu hardens the app so it's harder to study, and can sign requests with a key held in the phone's secure hardware. Neither stops every automated attack, but together they make your app much harder to imitate without being noticed.
Sources
- OWASP API Security Top 10 (2023): API6, Unrestricted Access to Sensitive Business Flows
- Android Developers: Overview of the Play Integrity API
- Apple Developer: Establishing your app's integrity (App Attest)
Figures are as reported by the sources above at the time of writing. Spotted an error? Email contact@mosecure.id and we will correct it.