Safeguard
Application Security•

ASVS 5.0 V16.5.3: Fail Securely and Never Fail Open

ASVS V16.5.3 requires graceful, secure failure on exceptions, so a validation error never lets a transaction go through. Patterns and tests.

Safeguard Security Team
4 min read

ASVS 5.0 V16.5.3 requires that the application fails gracefully and securely when an exception occurs. The example in the standard is a transaction that is processed despite an error in validation logic. That is a fail open condition. This is a Level 2 requirement.

The rule of thumb: the default result of any check must be denial. Only an explicit, successful check produces approval.

Why it matters

Code that sets approved = True first and flips it to false when a check fails will approve anything that throws before the flip. Attackers send inputs designed to cause exceptions: nulls, huge numbers, odd encodings. If the exception skips the check, they win.

How to implement it

Structure checks so approval happens at the end, and only on the happy path.

Risky:

def transfer(req):
    ok = True
    try:
        validate(req)          # raises on bad data
    except ValueError:
        ok = False
    # a TypeError from validate() is not caught, but a bare except below hides it
    ...

Safer:

def transfer(req):
    try:
        validate(req)
        check_limits(req.account, req.amount)
    except Exception:
        log.exception("transfer_rejected")
        raise TransferRejected()          # caller returns a generic 4xx or 5xx
    return execute(req)                   # reached only if nothing raised

Other habits that help:

  • Start permission variables as denied, and grant by assignment after a positive result.
  • Use finally only to release resources, never to complete a business action.
  • Make transactions atomic so a mid way error rolls back instead of leaving half a change.
  • Treat unknown enum values, missing fields and parse errors as invalid input.

Put a test beside every guard that throws on purpose, and add a pull request gate so changes to guards must pass those tests.

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

How to test it

For each security check (validation, authorization, signature, limit), write tests that force failure in unusual ways: the dependency throws, the input is null, the number overflows, the JSON is truncated. Assert the outcome is rejection and that no side effect occurred, such as a row written or a message sent.

Fuzz the validator with random bytes and confirm the process never reports success for garbage.

Reading code for fail open

When reviewing, look for these shapes: a boolean that starts true, a catch block that returns or continues without rethrowing, a default branch that grants access, a missing else after a check, and checks placed after the side effect they should guard. Authorization middleware deserves special attention, because a thrown exception inside it should end the request, not pass it along.

Transactions and partial failure

Failing securely includes leaving data consistent. If validation passes but the ledger write fails, the system must not report success, and it must not leave a debit without a credit. Use database transactions or a saga with compensating steps, and make operations idempotent so a retry cannot double apply. Return a clear failure to the caller and log enough to reconcile.

Graceful for the user, strict for the system

Graceful means the user sees a calm, generic message and can retry; it does not mean the system proceeds. Keep these separate in the design. The user interface can be forgiving while the backend remains strict.

Common mistakes

  • except Exception: pass around a guard.
  • Returning true from a helper in the error branch "to keep things moving".
  • Validation that runs after the action instead of before.
  • Authorization middleware that calls next() inside a catch block.

Quick check

What should the default value of an authorization flag be?

Denied. Set it to allowed only after a check completes successfully.

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.