Safeguard
Application Security•

CWE-206: Internal Behavior Leaks in Login and Multi-Step Checks

When one decision is made in two internal steps and each step is visible, attackers can attack them one at a time. CWE-206 and the login example.

Safeguard Security Team
3 min read

CWE-206, Observable Internal Behavioral Discrepancy, applies when a product combines several behaviors into a single result but lets outsiders see the individual behaviors. That visibility reveals internal state or decision points.

MITRE's own example is the one most developers know. A login has one outcome, success or failure. Internally it does two things: check whether the username exists, then check the password. If the product behaves differently depending on whether the username exists, the attacker only has to work on the password.

How it happens

The seam between steps shows. Examples from the record:

  • CVE-2003-0190: the product sends an error immediately when the user does not exist, instead of waiting until the password is provided, allowing username enumeration.
  • CVE-2005-2025: valid group names can be enumerated because the product responds if the name is valid and does not respond if not.
  • CVE-2001-1497: a GUI lets attackers tell alphanumeric from non-alphanumeric characters in a password, reducing the search space.
  • CVE-2002-2031: file existence leaks by watching whether an onerror handler fires.

What an attacker gains

Confidentiality and access control are affected: the attacker reads application data and bypasses a protection mechanism. Each leaked step cuts the guessing space.

How to find it

  1. For every multi-step check, list the steps and ask whether each one can fail visibly on its own.
  2. Test with a valid user and a bad password, then an invalid user and any password. Compare timing as well as content.
  3. Look for early returns. An error sent before all input has been collected is a sign.

How to fix it

MITRE's mitigation is to set up generic response pages for error conditions. The page should not disclose success or failure of a sensitive operation. A login page should not confirm that the account is right and the password wrong, because confirming the account makes brute force easier.

In practice: collect all input first, run every check, then return one answer for any failure.

user = db.find_user(email)
password_ok = check_password(user, password) if user else fake_check(password)
if not (user and password_ok):
    return error("Invalid email or password", 401)

The fake_check stands in for a dummy hash comparison so the missing-user path costs about the same time.

Quick check

Why does an early error for a missing user matter?

It tells the attacker the username is invalid before any password is tried, so they can sort real accounts from fake ones.

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.