Weakness reference
CWE-302

Authentication Bypass by Assumed-Immutable Data

This weakness occurs when an application trusts data it shouldn't—typically information stored on the client side or in places an attacker can modify—to verify…

01Summary

This weakness occurs when an application trusts data it shouldn't—typically information stored on the client side or in places an attacker can modify—to verify a user's identity. If the application assumes this data cannot be changed, an attacker can forge or alter it to bypass authentication entirely. This is a critical flaw because authentication is the first line of defense against unauthorized access.

02How It Happens

Authentication systems work by verifying that a user is who they claim to be. The vulnerability arises when developers assume certain data—such as cookies, hidden form fields, URL parameters, or local storage values—are tamper-proof and use them as the sole basis for granting access. In reality, an attacker can intercept, modify, or fabricate this data before sending it to the server. If the server never validates the data against a trusted source (like a session database or cryptographic signature), the attacker can simply change a user ID, role flag, or token to impersonate someone else or escalate privileges.

The root cause is a misunderstanding of the trust boundary: anything that originates from or passes through the client is inherently untrusted, even if it appears to be "locked down" or "read-only" in the user interface.

03Real-World Impact

Successful exploitation allows an attacker to log in as any user without knowing their password, access accounts with elevated privileges, or bypass multi-factor authentication checks. This can lead to unauthorized data access, account takeover, fraud, and compliance violations. In high-stakes applications (banking, healthcare, government), this type of flaw can expose sensitive personal information or enable financial theft.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request, session

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')
    
    # Assume password is correct (simplified for example)
    session['user_id'] = request.form.get('user_id')
    session['is_admin'] = request.form.get('is_admin')  # Client sends this!
    
    return "Logged in"

@app.route('/admin')
def admin_panel():
    if session.get('is_admin') == 'true':  # Trusts client-supplied value
        return "Admin panel"
    return "Access denied"

Why it's vulnerable:
The is_admin flag comes directly from the client (a form field or cookie) and is trusted without verification. An attacker can modify the request to set is_admin=true and gain admin access.

Fixed pattern
from flask import Flask, request, session
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(32)

# Simulated user database
USERS = {
    'alice': {'password_hash': 'hashed_pwd_1', 'is_admin': True},
    'bob': {'password_hash': 'hashed_pwd_2', 'is_admin': False}
}

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')
    
    # Verify credentials against server-side database
    if username in USERS and verify_password(password, USERS[username]['password_hash']):
        session['user_id'] = username
        # Store only the username; fetch role from database on each request
        return "Logged in"
    return "Invalid credentials"

@app.route('/admin')
def admin_panel():
    username = session.get('user_id')
    # Always check the server-side database, never trust client data
    if username and USERS.get(username, {}).get('is_admin'):
        return "Admin panel"
    return "Access denied"
Vulnerable pattern
<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    // Assume password is correct
    $_SESSION['user_id'] = $_POST['user_id'];
    $_SESSION['role'] = $_POST['role'];  // Client sends this!
}

if ($_SESSION['role'] === 'admin') {
    echo "Admin panel";
} else {
    echo "Access denied";
}
?>

Why it's vulnerable:
The role field is set directly from user input and trusted without server-side verification. An attacker can modify the POST data to set role=admin.

Fixed pattern
<?php
session_start();

$users = [
    'alice' => ['password_hash' => 'hashed_pwd_1', 'role' => 'admin'],
    'bob' => ['password_hash' => 'hashed_pwd_2', 'role' => 'user']
];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    // Verify credentials against server-side database
    if (isset($users[$username]) && password_verify($password, $users[$username]['password_hash'])) {
        $_SESSION['user_id'] = $username;
        // Do NOT store role in session; fetch it from database on each request
    }
}

// Always check the server-side database
$username = $_SESSION['user_id'] ?? null;
if ($username && isset($users[$username]) && $users[$username]['role'] === 'admin') {
    echo "Admin panel";
} else {
    echo "Access denied";
}
?>

05Prevention Checklist

Never trust client-supplied authentication data.
Do not use form fields, cookies, or URL parameters as the sole basis for identity or role decisions.
Store sensitive state server-side only.
Keep user roles, permissions, and authentication status in a server-side session store or database, not in cookies or hidden fields.
Validate every request against the database.
On each protected action, re-verify the user's identity and permissions by querying the authoritative server-side source.
Use cryptographically signed tokens if needed.
If you must pass data to the client, sign it with a server-side secret (e.g., JWT with HMAC) so tampering is detectable.
Implement proper session management.
Use framework-provided session handling (Flask sessions, PHP $_SESSION) with secure, server-side storage.
Test authentication logic with modified requests.
Use a proxy tool to intercept and alter authentication-related fields and verify the server rejects them.

06Signs You May Already Be Affected

Look for authentication checks that read directly from cookies, hidden form fields, or URL parameters without querying a database or session store. If you can modify a request (using browser developer tools or a proxy) to change a user ID, role, or permission flag and gain unauthorized access, your application is vulnerable. Check server logs for unusual privilege escalations or access patterns that don't match normal user behavior.

07Related Recent Vulnerabilities