Safeguard
Application Security•

ASVS 5.0 V5.2.4: Enforce Per-User File Quotas

A single user should not be able to fill your storage. Enforce a byte quota and a file count per user, and make the check atomic under concurrency.

Safeguard Security Team
4 min read

ASVS 5.0 V5.2.4 (level 3) requires a file size quota and a maximum number of files per user, so that one user cannot fill up storage with too many files or excessively large ones.

The per upload limit from V5.2.1 stops one huge request. This requirement stops the slower attack: a thousand small valid uploads, or a script that keeps sending files at the maximum allowed size until the bucket or the disk is full.

Why it matters

Storage costs money and has a ceiling. A free trial account with no quota is a free file host, or a way to run up your bill. On a shared disk, one account can bring down the service for everyone. Abuse does not need to be hostile either: a buggy client that retries uploads in a loop produces the same result.

Quotas also protect your own operations. Backups, scans and thumbnails all scale with the number of objects, so unlimited counts become a processing problem as well as a storage one.

How to implement it

Track usage per user in your database and check it before accepting a file, in the same transaction that records the new file so concurrent requests cannot race past the limit.

CREATE TABLE user_storage (
  user_id BIGINT PRIMARY KEY,
  bytes_used BIGINT NOT NULL DEFAULT 0,
  files_used INT NOT NULL DEFAULT 0,
  CHECK (bytes_used >= 0 AND files_used >= 0)
);

A conditional update does the check and the reservation in one statement:

UPDATE user_storage
SET bytes_used = bytes_used + $1, files_used = files_used + 1
WHERE user_id = $2
  AND bytes_used + $1 <= $3   -- byte quota
  AND files_used + 1 <= $4;   -- file quota
-- zero rows updated means quota exceeded: reject with 413 or 429

Reserve first, store the file second, and roll the reservation back if the store fails. On delete, subtract the size. Run a periodic job that recomputes usage from the real objects, so drift does not build up. Make quotas depend on the plan, return the remaining quota in API responses, and alert when an account hits a large share of its limit. Remember generated files, such as exports and thumbnails: if a user can trigger them, they count too.

How to test it

Set a small quota in a test environment, such as 3 files or 1 MB, then try to break it.

  1. Upload up to the limit and confirm the next upload is refused with a clear status.
  2. Delete a file and confirm there is room again.
  3. Fire 20 parallel uploads at the boundary, for example with xargs -P 20 curl .... The totals must never exceed the quota.
  4. Upload as two different users and confirm usage is separate.
  5. Trigger a feature that creates generated files and confirm it is counted.

Finally, compare the stored counter with a recount from the storage listing. A mismatch means your accounting has a leak.

Common mistakes

  • Checking usage with a read, then writing later, which allows races under parallel uploads.
  • Only a byte limit, with no count limit, or the other way round.
  • Never subtracting on delete, so legitimate users hit the quota.
  • Forgetting generated or derived files.
  • One global limit for the whole app instead of per user or per tenant.
  • No alerting, so abuse is found only on the invoice.

Quick check

How do you stop parallel uploads from exceeding a quota?

Reserve the space with one atomic conditional update before storing the file, and roll back if the store fails.

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.