πŸ›‘ OWASP Lab
API6:2023 High

Unrestricted Access to Sensitive Business Flows

Theory

API6 Unrestricted Access to Sensitive Business Flows is new to the OWASP API Top 10 in 2023. Unlike the other authentication/authorization categories, this one is about legitimate functionality being misused at scale. The endpoint works exactly as designed β€” the problem is that there are no business-logic controls preventing automated abuse.

What Makes a Business Flow "Sensitive"

  • Inventory / purchase flows β€” a scalper bot buys all limited-edition items the moment they go on sale; resells at 10x price
  • Voucher / gift card checking β€” bulk-checking thousands of codes per minute to find valid unredeemed cards
  • User enumeration at scale β€” calling a "check if email is registered" or "check if username exists" endpoint millions of times to build targeted phishing lists
  • Price scraping β€” competitors scrape all product prices every hour to undercut dynamically
  • Referral abuse β€” creating thousands of fake accounts to harvest referral bonuses
  • Free tier abuse β€” creating unlimited free accounts, each with its own free resource allocation

The Sneaker Bot Problem

# A limited-edition sneaker drops at 10:00 AM. 10,000 units available.
# A bot pre-loads all required data, monitors for the drop, and fires:

for _ in range(10000):
    r = requests.post("/api/v1/orders",
                      headers={"Authorization": f"Bearer {token}"},
                      json={"product_id": SNEAKER_ID, "quantity": 1})
    if r.status_code == 201:
        orders.append(r.json())

# In < 1 second, the bot buys all 10,000 units.
# Human users receive "out of stock" immediately after the drop.
# No authentication failure β€” every request uses a valid token and real account.

Gift Card / Voucher Enumeration

# VULNERABLE β€” no rate limit on code checking; no limit on batch size
@app.post("/api/v1/giftcards/check")
async def check_giftcards(request: Request):
    body = await request.json()
    codes = body.get("codes", [])   # attacker sends 10,000 codes in one request
    results = []
    for code in codes:
        card = db.get_giftcard(code)
        results.append({"code": code, "valid": bool(card), "value": card.value if card else 0})
    return results

# Attack: brute-force gift card numbers
for i in range(10000):
    code = f"RATC-{i:04d}-GIFT"
    r = requests.post("/api/v1/giftcards/check", json={"codes": [code]})
    if r.json()[0]["valid"]:
        print(f"Valid card: {code}")

Fixed Code

# FIXED β€” rate limiting + batch size cap + single-code-only enforcement
from slowapi import Limiter

limiter = Limiter(key_func=get_remote_address)

@app.post("/api/v1/giftcards/check")
@limiter.limit("5/minute")   # severely throttle this endpoint
async def check_giftcards(request: Request, body: GiftCardCheckRequest, user = Depends(get_current_user)):
    if len(body.codes) > 1:   # allow only one code per request
        raise HTTPException(400, "Only one code per request")
    code = body.codes[0]
    card = db.exec(select(GiftCard).where(GiftCard.code == code)).first()
    # Timing-safe response: same delay regardless of whether code is valid
    await asyncio.sleep(random.uniform(0.1, 0.3))   # prevent timing-based enumeration
    return {"valid": bool(card)}

Real-World Examples

  • Nike / Adidas SNKRS (2019–present) β€” Sneaker bots routinely buy out entire drops within milliseconds. Nike's SNKRS app introduced CAPTCHA and randomised checkout timing; bots adapted within days.
  • Airbnb (2018) β€” A researcher discovered the coupon check endpoint had no rate limiting; valid coupon codes could be enumerated by brute-force in minutes. $5,000 bounty.
  • Twitter (2017) β€” Phone number lookup endpoint with no rate limit allowed enumeration of registered phone numbers at scale

How to Fix β€” Checklist

  • Rate limiting specific to business flows β€” gift card checks: 5/minute; purchase: 10/minute; account creation: 3/hour per IP
  • Batch size caps β€” every endpoint accepting arrays should have a strict maximum count at the schema level
  • Device fingerprinting + behavioural analysis β€” detect inhuman request patterns (identical headers, sub-millisecond timing) and require CAPTCHA or block
  • Inventory locks β€” hold inventory reservation for a limited time window; release if checkout isn't completed
  • Queue flows for high-demand events β€” virtual waiting room (e.g. Cloudflare Waiting Room) serialises access during drops
  • Account age and trust scoring β€” newly created accounts face stricter limits; purchasing requires email verification or phone verification
Challenge 1

Bulk Gift Card Checking β€” Enumerate Valid Codes

The gift card validation endpoint accepts a list of codes with no size limit. Check hundreds of codes in a single request to enumerate valid ones.

Hint
POST /api/v1/giftcards/check with {"codes": ["RATC-0000-GIFT", ..., "RATC-FLAG-API6"]}