Race Conditions in APIs: Single-Packet Attacks and Idempotency
Cybersecurity
TOCTOU race conditions in payment, voucher, and refund APIs let attackers double-spend in milliseconds. Here's how to find them with Burp's single-packet attack and the locking patterns that make them disappear.
By Arjun Raghavan, Security & Systems Lead, BIPI · January 5, 2025 · 8 min read
Race conditions used to be hard to exploit at internet latency. James Kettle's single-packet attack changed that: by stacking 20-plus HTTP/2 requests into one TCP packet, an attacker can hit a server within microseconds and reliably win TOCTOU races.
Where to hunt
Anywhere a state check precedes a state change is a candidate. The highest-yield targets are voucher redemption, gift card balance deduction, refund issuance, withdrawal endpoints, two-factor enrollment, password reset token consumption, follow and unfollow toggles, and rate-limited signup or vote endpoints.
How to test for it
In Burp, capture the target request, send it to Repeater, and use the Send group in parallel feature to fire 20 copies in a single TCP packet over HTTP/2. Watch for any response that succeeds more than once when the business logic should permit only one success. Turbo Intruder gives you finer control via its engine='requests' and concurrentConnections=1 plus pipeline=True configuration.
- Voucher redemption: fire 20 redeem requests for the same code and check whether multiple grant credit.
- Refund: request a refund 20 times in parallel for the same order and watch for multiple refund credits.
- MFA enroll: race an MFA-enroll with an MFA-disable to land in a half-state where MFA bypass works.
- Withdrawal: request a withdrawal of 100 percent of balance from 20 parallel sessions.
- Promo signup: register the same email 20 times in parallel against an endpoint that grants signup credit.
For sub-state races, like creating a resource and consuming it before validation, use Burp's race-condition detection feature in 2024.x which automatically tags responses that diverge from the baseline timing distribution.
Detection
At the application layer, log every state-changing operation with a transaction ID and alert on duplicate transactions for the same idempotency key or unique resource within a 100ms window. At the database layer, monitor for SELECT FOR UPDATE bypasses and audit table rows showing the same balance debited twice. WAF cannot see this directly, but it can rate-limit per second per session, which buys headroom.
Remediation
- Use database row-level locks: SELECT ... FOR UPDATE inside a transaction that also performs the state change. Postgres advisory locks (pg_advisory_xact_lock) work well for cross-row coordination.
- Implement idempotency keys per Stripe's pattern: client sends a UUID in Idempotency-Key header, server stores the response keyed by user_id plus key for 24 hours, replays the stored response on retry.
- Use atomic database operations: UPDATE accounts SET balance = balance - 100 WHERE id = ? AND balance >= 100. The WHERE clause prevents overdraft regardless of concurrency.
- For distributed systems, use Redis SET NX with a short TTL as a mutex around the critical section, or adopt a saga pattern with explicit compensation.
- Move from check-then-act to act-then-check where possible: insert a row with a unique constraint, catch the duplicate-key error, treat as already-processed.
A real chain we found
On a fintech engagement, a refund endpoint validated order state with SELECT and then issued the refund without a lock. Twenty parallel single-packet requests produced 18 refund credits for one order. The fix was a four-line change: SELECT FOR UPDATE inside the existing transaction. Idempotency keys would have masked the bug for normal retry traffic but not for the parallel attack.
Validation
Re-run the single-packet attack and confirm only one operation succeeds while the others return a serialization failure or a 409 Conflict. Add a load test that fires 100 parallel requests against each state-changing endpoint and asserts exactly one success. Bake the test into CI so refactors cannot reintroduce the race.
Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.