Race Condition (TOCTOU)
Race conditions occur when multiple concurrent requests exploit a Time-of-Check to Time-of-Use window — allowing attackers to apply promo codes multiple times, double-spend balances, or claim rewards that should only be redeemable once.
What Is a Race Condition?
A race condition (TOCTOU — Time-of-Check to Time-of-Use) occurs when an application checks a condition, then acts on it, but doesn't prevent concurrent requests from exploiting the window between the check and the action.
In web applications, this typically means firing many simultaneous requests at an endpoint that should only succeed once — before the server has time to mark the resource as used.
How It Works
Consider a promo code redemption endpoint:
# Vulnerable implementation
def redeem_promo(code: str, user_id: str):
promo = db.get_promo(code) # 1. CHECK — is it valid?
if promo.used:
return error("Already used")
apply_discount(user_id, promo) # 2. USE — apply the discount
db.mark_used(promo.id) # 3. MARK — too late, race already wonAn attacker sends 15 simultaneous requests at exactly the same millisecond:
# All 15 fire concurrently before any single one completes step 3
for i in $(seq 1 15); do
curl -X POST /api/redeem -d '{"code":"SAVE50"}' &
doneMultiple requests pass step 1 before any completes step 3 — the discount is applied many times.
Common Targets
- Promo / coupon codes — single-use codes applied multiple times
- Gift card balance — spending the same balance before the deduction commits
- Referral bonuses — claiming a bonus for each concurrent signup
- Rate limits — bypassing "one free trial" or "one download" limits
- Inventory — purchasing the last item multiple times simultaneously
Real-World Impact
- Revenue loss — promo codes applied 10–100× instead of once
- Free tier bypass — multiple concurrent trial signups before rate limit kicks in
- Double-spend — cryptocurrency or credit balance exploited concurrently
- Inventory oversell — selling more items than in stock
How to Fix
Use atomic database operations — the gold standard:
-- Atomic claim: only one request can succeed
UPDATE promo_codes
SET used_by = $1, used_at = NOW()
WHERE code = $2 AND used_by IS NULL
RETURNING id;
-- If 0 rows returned, promo was already claimedUse database-level locks:
# PostgreSQL advisory lock — only one process proceeds at a time
with db.transaction():
db.execute("SELECT pg_advisory_xact_lock(hashtext($1))", [promo_code])
promo = db.get_promo(promo_code)
if promo.used:
raise ValueError("Already used")
# safe to proceedUse Redis atomic operations for high-throughput scenarios:
# SETNX — set if not exists — atomic in Redis
result = redis.setnx(f"promo:{code}:lock", user_id)
if not result:
return error("Already claimed")
redis.expire(f"promo:{code}:lock", 60) # TTL safety netIdempotency keys for payment APIs:
# Stripe / payment processor — idempotency prevents double-charge
stripe.PaymentIntent.create(
amount=1000,
idempotency_key=f"order-{order_id}" # same key = same result
)What VibeWShield Detects
VibeWShield's Race Condition scanner identifies endpoints containing keywords: redeem, coupon, promo, checkout, purchase, transfer, withdraw, claim, subscribe, invite, bonus, reward. It then fires 15 simultaneous requests via asyncio.gather().
If ≥30% of requests return HTTP 200/201 (more than one success when only one should be allowed), a race condition is flagged as High severity with the request count and success rate as evidence.
Free security scan
Test your app for Race Condition (TOCTOU)
VibeWShield automatically checks for Race Condition (TOCTOU) and 40+ other vulnerabilities using 63 scanners — in under 3 minutes, no signup required.
Scan your app free