Safeguard
Application Security•

ASVS 5.0 V2.3.2: Implement the Documented Business Limits

ASVS 5.0 V2.3.2 (Level 2) requires business limits to be enforced as documented. How to code per user and global limits, and how to test them.

Safeguard Security Team
3 min read

ASVS 5.0 V2.3.2 requires that business logic limits are implemented as the application's documentation says, so that logic flaws cannot be exploited. It is Level 2, and it pairs with V2.1.3.

Why it matters

A written limit that the code ignores is worse than none, because everyone believes it protects them. Abuse of unlimited coupons, repeated refunds, unlimited free trials and bulk resource creation all come from limits that exist on paper only.

How to implement it

Enforce limits in the service layer, next to the action, and make the check and the write one atomic step so concurrent requests cannot slip past it.

-- allow at most 5 payouts per user per day, atomically
INSERT INTO payouts (user_id, amount_cents, created_at)
SELECT :user_id, :amount, now()
WHERE (SELECT count(*) FROM payouts
       WHERE user_id = :user_id
         AND created_at > now() - interval '1 day') < 5;

Check the affected row count to see whether it was accepted. Put the limits in configuration with a name, so they match the document and can be audited. Track global limits with a shared counter, not per instance memory. Log every breach with the user and the limit name, and alert on bursts.

How to test it

Take the limits table from your documentation and test each row at the limit, one above it, and in parallel. A scripted run of 20 concurrent requests against a limit of 5 should result in exactly 5 successes.

Making limits observable

Build each limit as a named rule in code, such as payout_daily_user. Emit a metric and a log line with the rule name when it triggers. Then you can answer two questions that matter after an incident: which limits fire often, and which never fire. A limit that never fires may be set too high to matter, or may not be wired up at all.

For money and inventory, add reconciliation. A nightly job that totals payouts per user and compares with the documented caps will catch a limit that the code failed to enforce, including through a new endpoint that skipped the shared check.

Common mistakes

  • Counting in memory on one instance of many.
  • Check then act in two steps, which races.
  • Limits keyed only on IP address, which is easy to rotate.

Quick check

Why test with parallel requests?

Many limits fail only under concurrency, because two requests both read the old count before either writes.

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.