Safeguard
Application Security•

ASVS 5.0 V15.4.2: Make Check and Use One Atomic Step

ASVS V15.4.2 (Level 3) targets TOCTOU races. Combine the check and the action in one operation, with file and database examples and a concurrency test.

Safeguard Security Team
2 min read

ASVS 5.0 V15.4.2 requires that a check on a resource's state, such as existence or permission, and the action that depends on it happen as a single atomic operation, preventing time of check to time of use races. It is Level 3. Do not check and then act in two steps; let the operation itself enforce the condition.

Why it matters

Between a check and the action, the world can change. A file you confirmed was not a symlink gets replaced by one. A coupon you saw as unused is redeemed by ten parallel requests. The gap may last microseconds, but an attacker can repeat the attempt thousands of times.

How to implement it

For files, open with flags that make the check part of the call:

import os
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)

O_EXCL fails if the file exists, and O_NOFOLLOW refuses a symlink. No separate exists check is needed.

For database state, put the condition inside the statement:

UPDATE coupons
SET redeemed_by = :user_id, redeemed_at = now()
WHERE code = :code AND redeemed_by IS NULL;
-- then confirm exactly one row was affected

If the affected row count is zero, the coupon was already used. The same applies to stock levels, balances and one time tokens. Where a statement alone is not enough, use a transaction with the right isolation level or a row lock such as SELECT ... FOR UPDATE.

How to test it

Write a test that fires 20 to 50 identical requests at the same instant, for example against a single use coupon or a withdrawal, and assert that exactly one succeeds. For files, run a loop that swaps a path between a regular file and a symlink while the code under test runs.

Common mistakes

  • A read then write sequence with no lock or condition.
  • Checking permission at the start of a long request and acting on the result minutes later.
  • Relying on application level flags when several instances run.

Quick check

Is a transaction always enough?

Not by default. Under common isolation levels two transactions can both read the same state. Use a conditional update or an explicit lock.

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.