Safeguard
Application Security•

CWE-807: Trusting Cookies and Hidden Fields in Security Decisions

CWE-807 is the habit of letting a cookie, header or hidden field decide who gets in. Real admin bypasses show why, and server side state is the fix.

Safeguard Security Team
4 min read

CWE-807 is reliance on untrusted inputs in a security decision. The product has a protection mechanism, but that mechanism depends on the existence or value of an input an outsider can change. Change the input and the protection falls away.

It hits web applications hardest, though MITRE marks it as not language specific and not technology specific. Any system that asks the client "are you an admin?" and believes the answer belongs here.

How it happens

Developers often assume some inputs cannot be modified: cookies, environment variables, hidden form fields. They can. An attacker with a customized client, or just browser developer tools, sets whatever value they like. If authentication or authorization reads that value without any integrity check, the attacker writes their own pass.

MITRE's observed examples read like a pattern catalog.

  • CVE-2009-1619: an attacker gains admin privileges by setting an admin cookie to 1.
  • CVE-2009-0864: a content management system grants admin privileges when a login cookie is set to OK.
  • CVE-2008-5784: an e-dating application grants admin by setting the admin cookie to 1.
  • CVE-2009-1549: authentication is bypassed by setting a cookie to a specific value.

A minimal vulnerable version in Python with Flask looks like this.

# Vulnerable: the client says whether it is an admin
@app.route("/admin")
def admin():
    if request.cookies.get("admin") == "1":
        return render_template("admin.html")
    abort(403)

Anyone can send Cookie: admin=1.

What an attacker gains

MITRE says attackers can bypass the security decision to reach whatever it protects. The impact depends on context, and the record lists bypassing protection mechanisms, gaining privileges or assuming another identity, plus effects on confidentiality, access control and availability. In the cookie examples above, that meant full admin access.

How to find it

MITRE notes this weakness does not usually appear many times in one package, so manual white box review can cover it well within a limited time. Look at every place the code makes an allow or deny decision and trace where the deciding value came from. If the answer is a cookie, header, query parameter, hidden field or environment variable the client can influence, flag it.

Other approaches from the record: binary or bytecode analysis for compiled software, and dynamic analysis with manual interpretation. A practical test is simple. Capture a request in a proxy, edit the cookie or hidden field, and replay it.

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

How to fix it

MITRE's mitigations come down to keeping authority on the server.

  1. Store state and sensitive data on the server only. Keep a definitive record of user state, with defined rules for legitimate state transitions. Users should affect state only through legitimate actions.
  2. If state must live on the client, protect it. Do not store it without encryption and integrity checking, or a server side mechanism that catches tampering. A message authentication code such as HMAC over the exposed data guarantees it has not been modified. Use a strong hash function.
  3. Use a framework that maintains state for you. With a stateless protocol like HTTP, a vetted session mechanism beats hand rolled cookies. MITRE cautions that convenience features may not have been built with security in mind.
  4. Repeat client side checks on the server. Attackers can modify values after client checks run, or remove the checks by changing the client.
# Fixed: the server session is the source of truth
@app.route("/admin")
def admin():
    user = load_user(session.get("user_id"))   # server side lookup
    if user and user.is_admin:
        return render_template("admin.html")
    abort(403)

The session cookie is only a random identifier. The privilege lives in your database.

Quick check

Is an encrypted cookie enough?

Encryption alone is not integrity. MITRE calls for encryption and integrity checking, or a server side way to catch tampering, such as an HMAC. Keeping the privilege on the server is simpler and safer.

Does a client side check count as a security control?

Only as a convenience. Attackers can bypass or remove it, so the same check has to run on the server.

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.