Safeguard
Application Security•

ASVS 5.0 V6.6.3: Rate Limit Code Guessing on OOB Authentication

Code based out of band authentication needs rate limiting against brute force, and ideally 64 bit codes. A working limiter, tests, and mistakes. Level 2.

Safeguard Security Team
3 min read

ASVS 5.0 V6.6.3 requires code based out of band authentication to be protected against brute force with rate limiting. It also suggests considering codes with at least 64 bits of entropy. This is a Level 2 requirement.

Why it matters

A six digit code has a million values. At a hundred requests per second, an attacker covers that in under three hours, and a lucky guess comes much sooner. Without limits, the short life of the code does not save you, because attackers can request fresh codes and keep trying.

How to implement it

Limit attempts per code, per account and per source. Count failures server side, in shared storage so all instances agree.

import redis
r = redis.Redis()

MAX_PER_CODE = 5
MAX_PER_ACCOUNT_HOUR = 20

def guard(attempt_id: str, user_id: str):
    per_code = r.incr(f"oob:code:{attempt_id}")
    r.expire(f"oob:code:{attempt_id}", 600)
    per_acct = r.incr(f"oob:acct:{user_id}")
    r.expire(f"oob:acct:{user_id}", 3600)
    if per_code > MAX_PER_CODE or per_acct > MAX_PER_ACCOUNT_HOUR:
        raise TooManyAttempts()   # HTTP 429, and invalidate the code

def on_failure(attempt_id):
    if r.get(f"oob:code:{attempt_id}") and int(r.get(f"oob:code:{attempt_id}")) >= MAX_PER_CODE:
        db.attempts.invalidate(attempt_id)

After the cap, kill the code so the attacker must start over and trigger a new message. Also limit how many codes can be requested per hour. Use longer codes (64 bits is about 20 digits or 11 characters of base32) for mechanisms where limits are hard to enforce, such as links.

Remember that the response must not reveal whether the account exists (V6.3.8) and that errors must look the same under limiting.

How to test it

  1. Submit wrong codes in a loop and confirm lockout or HTTP 429 after your limit, not after hundreds.
  2. Confirm the correct code no longer works once the attempt cap has been hit.
  3. Request codes repeatedly and confirm a request cap.
  4. Rotate source IPs but keep the same account and confirm the account limit still applies.

Common mistakes

  • Limiting by IP only, which bots bypass with proxies.
  • Counters in server memory, which reset on restart or differ across instances.
  • A limit that resets when a new code is issued.
  • Returning the remaining attempts, which helps the attacker plan.

Try it yourself

API scanning shows whether an endpoint keeps accepting guesses without throttling.

Click through it at your own pace, or open it full screen.

Quick check

Is IP based rate limiting enough?

No. Attackers spread guesses across many addresses. Limit per code and per account as well.

What should happen to a code after too many wrong guesses?

It should be invalidated, so further guesses fail even if one happens to be right.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.