Race Condition (TOCTOU)
detect, understand, remediate
Race conditions exploit timing gaps between checking a condition and using the result, allowing attackers to send parallel requests that bypass limits, duplicate transactions, or corrupt application state.
No credit card required. Free plan available forever.
What is a race condition?
A race condition (also known as a Time-of-Check to Time-of-Use, or TOCTOU, vulnerability, classified under CWE-362) occurs when the behavior of a system depends on the sequence or timing of uncontrollable events, such as concurrent threads or parallel HTTP requests. When two or more operations attempt to access and modify the same shared resource simultaneously, the outcome becomes unpredictable, often allowing attackers to bypass security checks entirely.
In web applications, race conditions are especially dangerous in operations that involve a check followed by an action. For example, verifying a user's account balance before processing a withdrawal. If an attacker sends multiple withdrawal requests at the exact same moment, all requests may pass the balance check before any single request deducts the funds, resulting in a negative balance or unauthorized transfers. These vulnerabilities are closely related to broken access control issues where the enforcement of business rules fails under concurrency.
Race conditions are notoriously difficult to discover through conventional testing because they are timing-dependent and non-deterministic. A vulnerable endpoint might work correctly 999 out of 1,000 times, only failing when requests arrive within a microsecond window. This makes them a favorite target for sophisticated attackers who understand that business logic flaws often hide in the gaps between concurrent operations.
How it works
Identify concurrent operation
The attacker finds an operation that involves a check-then-act pattern, such as redeeming a coupon, transferring funds, or voting. These operations read state, validate it, and then modify it in separate steps.
Send parallel requests
Using tools or scripts, the attacker sends multiple identical requests simultaneously, timing them to arrive at the server within milliseconds of each other to maximize the chance of overlapping execution.
Exploit timing window
All parallel requests pass the validation check (e.g., "does the coupon still exist?" or "is the balance sufficient?") because none of them have modified the state yet. Each request sees the original, unmodified data.
Achieve unintended state
Every request completes its action, resulting in a coupon being redeemed multiple times, funds being withdrawn beyond the balance, or votes being counted more than once, all violating the application's intended business rules.
Common causes
Missing locks or mutexes
Critical sections of code that read and write shared state are not protected by database locks, mutexes, or semaphores, allowing multiple threads or processes to execute them simultaneously.
Non-atomic check-then-act patterns
The application separates the validation step (checking if an action is allowed) from the execution step (performing the action) into distinct database queries or API calls, creating a window where state can change between the two.
Optimistic concurrency without validation
Applications use optimistic concurrency control (no locking on read) but fail to verify that the data has not been modified by another process before committing the write, allowing stale reads to produce duplicate actions.
Shared mutable state
In-memory caches, session stores, file systems, or database rows are modified by concurrent request handlers without synchronization, leading to lost updates and inconsistent state across requests.
How to detect it
Automated detection
- SecPortal's authenticated scanning includes concurrent request modules that automatically send parallel requests to state-changing endpoints and compare responses for inconsistencies
- Static analysis tools can identify non-atomic check-then-act patterns in source code, flagging database reads followed by conditional writes without locking mechanisms
- Automated time-of-check analysis detects endpoints where validation queries and mutation queries are executed in separate transactions, indicating potential race windows
Manual testing
- Send 10 to 50 identical requests simultaneously using tools like Turbo Intruder or curl with parallel flags, targeting coupon redemption, fund transfer, or other state-changing endpoints
- Compare database state after parallel requests to verify whether duplicate records were created, balances went negative, or counters incremented beyond expected limits
- Introduce artificial delays in the application (via debugging proxies) between the check and act phases to widen the race window, making the vulnerability easier to reproduce consistently
How to fix it
Use database-level locks
Apply SELECT ... FOR UPDATE or advisory locks to ensure that only one transaction can read and modify a resource at a time. This serializes access to critical data and eliminates the race window between check and act.
Implement atomic operations
Combine the check and update into a single atomic database operation. For example, use UPDATE accounts SET balance = balance - 100 WHERE balance >= 100 instead of separate SELECT and UPDATE queries.
Add idempotency keys
Require clients to send a unique idempotency key with each request. Store these keys server-side and reject duplicate requests, ensuring that even if parallel requests arrive, only the first one is processed.
Use serializable transactions
Configure critical database operations to use SERIALIZABLE isolation level, which guarantees that concurrent transactions produce the same result as if they were executed one at a time.
Apply optimistic locking with retries
Add a version column to database rows and include it in UPDATE WHERE clauses. If the version has changed since the row was read, the update fails and the application retries with fresh data, preventing stale-read exploits.
Compliance impact
Related vulnerabilities
Test for race condition vulnerabilities
SecPortal sends parallel requests to detect TOCTOU flaws, double-spend bugs, and limit bypass conditions. Start free.
No credit card required. Free plan available forever.