Race Conditions
Also known as: TOCTOU · Time-of-check to time-of-use
A race condition occurs when an application's correctness depends on the timing or ordering of concurrent operations, and that ordering isn't safely enforced. In web applications, the most common exploitable form is a time-of-check to time-of-use (TOCTOU) gap: the server checks a condition - a balance, a rate limit, a coupon's redemption count - and then acts on it in a separate step, with no atomic lock between the two. An attacker who fires many requests at nearly the same instant can slip multiple actions through that gap before any of them updates the state the check relies on.
How it works
- 1The application performs a check (e.g. "does this coupon still have redemptions left?") and then a separate write (e.g. "redeem it") without locking the resource in between.
- 2An attacker sends many copies of the same request in a tight burst, often using HTTP/2 single-packet or connection-warming techniques to land them within the same processing window.
- 3Each request reads the same pre-write state during its check, so all of them pass validation before any of them commits the update.
- 4Every request proceeds as if it were the only one, letting the attacker redeem a one-time coupon multiple times, withdraw funds beyond a balance, or bypass a use-once action limit.
Example
POST /api/coupons/WELCOME10/redeem HTTP/2
Authorization: Bearer <token>
# Sent 50 times in a single TCP packet (HTTP/2 request batching)
# All 50 pass the "not yet redeemed" check before any commitsImpact
Race conditions let an attacker bypass business rules that assume single-threaded execution: redeeming a discount code repeatedly, withdrawing more than an account balance, registering for a limited resource more times than allowed, or bypassing an authentication rate limit entirely.
Remediation
- Make the check-then-act sequence atomic - use database-level locking (SELECT ... FOR UPDATE), unique constraints, or compare-and-swap operations instead of separate read/write steps.
- Enforce limits with atomic counters or the database's own constraint system, not application-layer conditionals.
- Rate-limit and deduplicate requests at the API gateway as a second layer of defense, not the only one.
- Test critical flows (payments, redemptions, authentication) under real concurrency, not just sequentially.
Detection & testing
Fire the same request dozens of times concurrently (single-packet or connection-warmed bursts) against endpoints that check-then-act on a limited resource, and confirm the server only ever commits one successful outcome.
Impactr tests for Race Conditions the way an attacker would - investigating, chaining it with related flaws, and proving impact with a reproducible exploit before it reaches your report.
Test my app for TOCTOUTools
Frequently asked questions
Why do HTTP/2 requests make race conditions easier to exploit?
HTTP/2 allows many requests to be sent within a single TCP packet, arriving at the server almost simultaneously. This 'single-packet attack' technique narrows timing windows that would otherwise be too small to reliably hit over HTTP/1.1's sequential connections.