Safeguard
DevSecOps

Why Python assert in Production Code Is a Security Risk

Using Python assert in production code is risky because assertions are stripped when Python runs optimized. Any security check written as an assert simply disappears.

Priya Mehta
DevSecOps Engineer
5 min read

Using Python assert in production code is a genuine security risk because the interpreter removes every assert statement when it runs in optimized mode, so any validation or security check you wrote as an assertion silently ceases to exist. This is not a style opinion. It is documented behavior: run Python with the -O flag or set PYTHONOPTIMIZE, and the special __debug__ constant becomes False, which causes the compiler to strip all assert statements from the bytecode entirely. If your input validation lived inside those asserts, your validation is gone.

What actually happens with -O

The mechanism is simple and worth seeing directly. Consider this function:

def transfer(amount, balance):
    assert amount > 0, "amount must be positive"
    assert amount <= balance, "insufficient funds"
    return balance - amount

Run normally, the asserts fire. Run with optimization, they vanish:

python script.py        # asserts active
python -O script.py     # asserts stripped — no checks at all

Under -O, transfer(-500, 100) returns 600. The negative-amount guard and the balance check were compiled out. An attacker who can influence how your process is launched, or who simply benefits from a deployment that runs optimized Python for performance, gets an application with its guardrails removed. The use of assert in Python is meant for exactly one thing, and this is why.

What assert is for

Assertions exist to catch programmer errors, the "this should never happen" conditions that indicate a bug in your own logic. They are a debugging aid that documents an invariant and blows up loudly during development and testing if that invariant is violated. That is a legitimate and useful role.

The line is this: assert for things that should be impossible if your code is correct; never assert for things that can happen because of external input. A malformed API request, a missing config value, an unauthenticated user, a file that is not where you expected, these are all runtime conditions, not programmer errors, and none of them should be guarded by an assertion.

The security-specific failure modes

Two categories of misuse are dangerous enough to call out.

Authorization and authentication checks. The worst possible use of assert is something like:

# NEVER do this
assert user.is_authenticated, "not logged in"
assert user.has_permission("admin"), "forbidden"

Under optimized Python, both checks disappear and every request is treated as an authenticated admin. This is a textbook privilege-escalation bug introduced by an idiom that looks harmless.

Input validation. Validating request payloads, sanitizing paths, or enforcing size limits with assert has the same problem. The validation evaporates in production and you are left processing whatever arrives.

Static analysis catches this pattern. A SAST tool or a linter rule such as Bandit's B101 will flag assert usage in application code precisely because of this risk. It is one of the cheapest wins in a secure-coding baseline.

What to use instead

Replace security-relevant asserts with explicit checks that raise real exceptions, which are never stripped:

def transfer(amount, balance):
    if amount <= 0:
        raise ValueError("amount must be positive")
    if amount > balance:
        raise InsufficientFundsError()
    return balance - amount

For request validation, use a validation library rather than hand-rolled checks. Pydantic, marshmallow, or a framework's built-in serializers validate input declaratively and raise on failure regardless of optimization level:

from pydantic import BaseModel, PositiveInt

class Transfer(BaseModel):
    amount: PositiveInt

For authorization, use your framework's decorators and permission classes, which are designed to fail closed. Django's @login_required, DRF permission classes, and Flask-Login's protections all raise or redirect rather than relying on a statement that can be compiled away.

But does anyone actually run with -O?

Yes, and that is the trap. Many production deployments run optimized Python for the modest performance and memory benefit, and some container base images or WSGI configurations enable it without the application author realizing. Even if your deployment does not use -O today, you cannot guarantee that every future deployment, every teammate's Docker image, or every downstream consumer of your library will avoid it. Writing correctness- and security-critical logic that depends on an interpreter flag being off is a fragile assumption that will eventually be violated.

A quick audit

Grep your codebase for assert statements and triage them:

grep -rn "assert " --include="*.py" .

Any assert that checks external input, enforces a security boundary, or has a side effect belongs on the list to fix. Asserts that document a true internal invariant in non-critical code can usually stay, though many teams adopt a blanket "no assert in application code" rule and confine assertions to test suites, which is a defensible simplification. Our academy covers building a secure-coding baseline that includes rules like this one.

FAQ

Why is assert removed in production Python?

When Python runs with the -O optimization flag or PYTHONOPTIMIZE is set, the __debug__ constant is False and the compiler strips all assert statements from the bytecode. This is intended behavior, not a bug.

Is it ever okay to use assert in Python?

Yes, for catching programmer errors and documenting internal invariants, the "this should never happen" conditions, mainly in development and tests. It is not okay for validating external input or enforcing security checks.

What should I use instead of assert for validation?

Raise explicit exceptions with if checks, or use a validation library like Pydantic or marshmallow. For authorization, use your web framework's permission decorators and classes, which fail closed and are never stripped.

How can I detect risky assert usage automatically?

Static analysis tools flag it. Bandit's B101 rule specifically reports assert statements in code, and most SAST configurations include it. Add the rule to CI so new asserts in application code are caught in review.

Never miss an update

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