A race condition lets somebody do once-only things more than once: redeem a single-use discount five times, withdraw the same store credit twice, or book the last seat simultaneously with another customer. The logic is correct when requests arrive one after another. Send them together and the checks all pass before any of them finish. OWASP treats this as a business logic flaw, and no scanner will find it for you.
Where the window opens
The gap sits between reading a value and writing it back. Application code checks that a voucher has not been used, then marks it used a few milliseconds later, and in between it is still available to every other request. Anything with a limit is a candidate: promotional codes, loyalty points, referral bonuses, stock levels, Available booking times and withdrawal balances. Payment flows deserve particular attention because the money leaves immediately while the ledger update happens afterwards, which is the same pattern with a financial consequence attached.
How a tester triggers it
Sending twenty identical requests as fast as possible is the crude version and it often works. The refined version, which testers now use routinely, sends the requests so they arrive in the same instant, using a single connection to eliminate network jitter. Modern tooling in interception proxies does this in a couple of clicks. When a flaw exists the result is unmistakable: a voucher redeemed four times, a balance that went negative, or two confirmations for one slot. It is one of the few findings where the proof needs no explanation for a business audience.
“Retail clients often already know about this and think of it as an accounting nuisance rather than a security finding. It stops being a nuisance when someone automates it. We have seen a single-use welcome credit drained several thousand pounds in an afternoon, and the ledger looked perfectly consistent afterwards, because every individual transaction was valid.”
William Fieldhouse, Director, Aardwolf Security Ltd

Fixes that hold, and one that does not
Solve it at the data layer. A unique constraint on the redemption record makes the second attempt fail regardless of timing, and it is the simplest durable fix. Row-level locking with a select for update, or an atomic conditional update that decrements only while stock remains, closes the window inside a transaction. Idempotency keys prevent a repeated request from being processed twice, which also protects against duplicate submissions from impatient users. Rate limiting is not a fix. It reduces how many attempts an attacker can make and does nothing about two requests arriving together.
Testing for it deliberately
Add concurrency cases to your own test suite for every limit that matters, because functional tests run sequentially and will always pass. Ask for these paths by name when you scope application penetration testing, since a tester who does not know which limits are commercially important may test the checkout and miss the loyalty scheme. Where the same operations are available through a mobile client or a partner integration, cover those too with API penetration tests, as the protective logic often lives in the web front end and not in the endpoint behind it.
Frequently asked questions about race conditions
These questions come up when this finding reaches a development team.
Does load testing find these?
No. Load testing measures whether the system copes with volume. A race condition needs two specific requests to collide on the same record, which is a correctness problem rather than a capacity one.
Are serverless applications more exposed?
They can be, because many instances run in parallel by design and there is no shared process memory to lock against. The answer is the same: enforce the constraint in the database rather than in the application.
