A race condition is a bug of timing, and a security-relevant one often takes the form of TOCTOU: time-of-check to time-of-use. You check a condition, a permission, a balance, a file, and then act on it a moment later, and if the state can change in between, an attacker can exploit that gap. On mobile this shows up as double-submitted payments, concurrent requests slipping past a check, or a file swapped between validation and use. The fix is to make the check and the action atomic, and to enforce the critical decision on the server. Here is what TOCTOU and race conditions are, where they show up, and how to prevent them.
Short answer
A TOCTOU (time-of-check to time-of-use) bug is a race condition where a security check is performed at one moment and the checked resource is used later, and the state changes in between, letting an attacker exploit the gap. Per OWASP, examples on mobile include double-submitted requests racing a server-side check, concurrent operations slipping past a one-time check, or a resource swapped between validation and use. The fix is to make the check and the use atomic, enforce critical decisions on the server with transactional logic or locking, use idempotency for operations that must happen once, and not rely on a client-side check that can be invalidated before the action. Do not assume state checked earlier still holds when you act.
What you should know
- TOCTOU is a timing gap: state changes between the check and the use.
- Race conditions exploit concurrency: simultaneous operations slip past checks.
- Client-side checks are racy: and bypassable; enforce server-side.
- Make check and use atomic: so nothing changes in between.
- Use idempotency for once-only actions: like payments.
What is a TOCTOU or race condition?
It is a flaw where the correctness of an operation depends on timing that an attacker can influence. In a TOCTOU bug specifically, your code checks a condition, that a user is authorized, that a balance is sufficient, that a file is safe, and then performs an action based on that check a moment later, and if the state can change between the check and the use, the action proceeds on a stale assumption. A race condition more broadly is when concurrent operations interleave in a way that breaks an assumption, such as two requests both passing a one-time check before either completes. On mobile, these often span the client and server: the client submits, the server checks and acts, and if checks and updates are not atomic, simultaneous or replayed requests can slip through. The unifying issue is trusting that a checked state still holds when you act on it.
Where does it show up in mobile apps?
In gaps between checking and acting, especially across client and server. The table lists common cases.
| Scenario | The race |
|---|---|
| Double-submitted payment | Two requests both pass the balance or one-time check |
| Concurrent redemption | A coupon or reward claimed more times than allowed |
| File checked then used | A file swapped between validation and use |
| Client check then server action | The client's check is stale or bypassed by the time the server acts |
| Auth state checked then trusted | State changes between the check and the protected action |
The recurring pattern is a one-time or conditional action that is checked and then performed non-atomically, so duplicate or concurrent attempts can both succeed. Payment and redemption flows are classic, where a user submitting the same request twice quickly, or many times concurrently, can get double value if the server checks and debits in separate, non-atomic steps. The file case is the textbook TOCTOU, where what is checked is not what is ultimately used.
How do you prevent it?
Make the critical check-and-act atomic, and enforce it on the server. The core fix is to ensure the check and the action that depends on it happen as one indivisible operation, so nothing can change in between, typically through a database transaction, a lock, or an atomic compare-and-set on the server rather than a check followed by a separate update. For once-only operations like payments, use an idempotency key so a duplicate or replayed request is recognized and not processed twice. Enforce these decisions on the server, since a client-side check is racy and bypassable, and the client cannot guarantee atomicity against an attacker controlling the requests. Do not act on a previously checked state without revalidating it atomically at the point of use, and for files, operate on the same handle you validated rather than re-resolving a path that could have changed. The principle is to remove the gap between check and use where it matters.
What to watch out for
The first trap is checking a condition on the client and trusting it when the server acts, since the client check is racy and bypassable; enforce server-side. The second is a payment or redemption flow that checks and updates non-atomically, letting duplicate or concurrent requests double-spend; use transactions and idempotency. The third is validating a file then using a path that could have changed. Race conditions are logic and concurrency flaws fixed on your server and in your code, so a pre-submission scan such as PTKD.com (https://ptkd.com), which reads the binary against OWASP MASVS, surfaces the app's endpoints and behavior but the atomic enforcement is yours to implement. Server-side transactional logic is the fix.
What to take away
- A TOCTOU bug is a race condition where state changes between a security check and the use of the checked resource, which an attacker can exploit.
- On mobile it shows up as double-submitted payments, concurrent redemptions, swapped files, or stale client checks the server trusts.
- Make the check and the action atomic with transactions, locks, or compare-and-set, enforce it server-side, and use idempotency for once-only operations.
- Race conditions are fixed in your server and code; use a pre-submission scan such as PTKD.com to understand the app's endpoints while you implement atomic enforcement.

