Authentication Bypass by Assumed-Immutable Data

Description

Authentication Bypass by Assumed-Immutable Data is a vulnerability that occurs when an authentication scheme uses key data elements that developers incorrectly assume cannot be controlled or modified by attackers. Common examples include trusting client-side cookies, hidden form fields, environment variables, or HTTP headers for authentication decisions. While these mechanisms may seem difficult to modify from a legitimate user's perspective, attackers can easily manipulate them using browser developer tools, proxy interceptors, or custom HTTP clients. Any data that originates from or passes through the client cannot be trusted for security decisions without server-side validation.

Risk

Relying on assumed-immutable data for authentication creates trivially exploitable vulnerabilities. Attackers can use readily available tools like browser developer consoles, HTTP proxy interceptors, or simple command-line utilities to modify cookies, hidden fields, and headers. The risk is severe because exploitation requires minimal technical skill yet grants full authentication bypass. Real-world exploits have demonstrated attackers gaining administrative access simply by setting a cookie value to "true" or modifying a hidden form field. These vulnerabilities often persist undetected because they may work correctly during normal testing - only targeted manipulation reveals the flaw. The impact extends from unauthorized data access to complete system compromise depending on the access level controlled by the vulnerable mechanism.

Solution

Never trust client-provided data for authentication decisions without server-side verification. Store authentication state exclusively server-side using cryptographically secure session identifiers. If client-side tokens must be used, sign them cryptographically (e.g., using HMAC or JWT with signatures) and verify signatures server-side before trusting any claims. Implement proper session management with server-side session stores. Validate and sanitize all input from clients, treating hidden form fields, cookies, and headers with the same suspicion as visible user input. Use framework-provided authentication mechanisms rather than implementing custom solutions. Apply defense in depth - even if one layer is bypassed, additional checks should prevent unauthorized access.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Attackers can bypass authentication entirely by modifying data assumed to be immutable, gaining unauthorized access as any user including administrators.
Integrity, ConfidentialityScope: Integrity, Confidentiality

With authentication bypassed, attackers can access sensitive data, modify records, and perform any action the impersonated user could perform.

Example Code

Vulnerable Code (Java)

The following examples demonstrate authentication bypass through assumed-immutable data:

// Vulnerable: Trusting cookie value for authentication
import javax.servlet.http.*;

public class VulnerableAuthServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) {

        // Vulnerable: Reading authentication state from cookie
        Cookie[] cookies = request.getCookies();
        boolean authenticated = false;
        String role = "user";

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                // Vulnerable: Trusting client-controlled cookie
                if ("authenticated".equals(cookie.getName())) {
                    authenticated = Boolean.parseBoolean(cookie.getValue());
                }
                if ("role".equals(cookie.getName())) {
                    role = cookie.getValue();  // Attacker sets to "admin"
                }
            }
        }

        // Attacker simply sets authenticated=true cookie
        if (authenticated) {
            if ("admin".equals(role)) {
                // Full admin access from just modifying cookies!
                showAdminPanel(response);
            } else {
                showUserDashboard(response);
            }
        } else {
            redirectToLogin(response);
        }
    }
}
# Vulnerable: Trusting hidden form field for authorization
from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/transfer', methods=['POST'])
def transfer_funds():
    # Vulnerable: User ID from hidden form field
    user_id = request.form.get('user_id')  # Hidden field in form
    amount = float(request.form.get('amount'))
    target_account = request.form.get('target_account')

    # Attacker modifies hidden user_id to impersonate another user
    # No server-side verification of actual logged-in user

    perform_transfer(user_id, target_account, amount)
    return "Transfer completed"

@app.route('/admin', methods=['GET'])
def admin_panel():
    # Vulnerable: Trusting query parameter for admin check
    is_admin = request.args.get('admin', 'false')

    # Attacker simply visits /admin?admin=true
    if is_admin.lower() == 'true':
        return render_template('admin_panel.html')
    else:
        return "Access Denied", 403
<?php
// Vulnerable: Trusting multiple assumed-immutable sources

// Vulnerable: Cookie-based authentication
$authenticated = isset($_COOKIE['logged_in']) && $_COOKIE['logged_in'] === 'true';
$username = $_COOKIE['username'] ?? '';

// Vulnerable: Hidden form field for authorization
$user_level = $_POST['user_level'] ?? 'guest';

// Vulnerable: HTTP header for admin bypass
$is_admin = isset($_SERVER['HTTP_X_ADMIN']) && $_SERVER['HTTP_X_ADMIN'] === 'true';

if (!$authenticated) {
    header('Location: /login.php');
    exit;
}

// Attacker sets X-Admin: true header and gains admin access
if ($is_admin || $user_level === 'admin') {
    include 'admin_dashboard.php';
} else {
    include 'user_dashboard.php';
}
?>

Fixed Code (Java)

// Fixed: Server-side session management for authentication
import javax.servlet.http.*;
import java.security.SecureRandom;
import java.util.*;

public class SecureAuthServlet extends HttpServlet {

    // Server-side session store
    private static final Map<String, UserSession> sessions =
        new ConcurrentHashMap<>();

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) {

        // Fixed: Get session ID from cookie, validate server-side
        String sessionId = null;
        Cookie[] cookies = request.getCookies();

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("session_id".equals(cookie.getName())) {
                    sessionId = cookie.getValue();
                    break;
                }
            }
        }

        // Fixed: Look up session data server-side
        UserSession session = (sessionId != null) ? sessions.get(sessionId) : null;

        // Fixed: Validate session exists and is not expired
        if (session == null || session.isExpired()) {
            redirectToLogin(response);
            return;
        }

        // Fixed: Get role from server-side session, not from client
        if ("admin".equals(session.getRole())) {
            showAdminPanel(response);
        } else {
            showUserDashboard(response);
        }
    }

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // Authenticate against database
        User user = authenticateUser(username, password);

        if (user != null) {
            // Fixed: Create server-side session
            String sessionId = generateSecureSessionId();
            UserSession session = new UserSession(user.getId(), user.getRole());
            sessions.put(sessionId, session);

            // Send only session ID to client (not auth state)
            Cookie sessionCookie = new Cookie("session_id", sessionId);
            sessionCookie.setHttpOnly(true);
            sessionCookie.setSecure(true);
            sessionCookie.setMaxAge(3600);
            response.addCookie(sessionCookie);

            response.sendRedirect("/dashboard");
        } else {
            response.sendRedirect("/login?error=invalid");
        }
    }

    private String generateSecureSessionId() {
        byte[] bytes = new byte[32];
        new SecureRandom().nextBytes(bytes);
        return Base64.getUrlEncoder().encodeToString(bytes);
    }
}
# Fixed: Server-side session management
from flask import Flask, request, session, redirect, url_for
from functools import wraps
import secrets

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

# Server-side session store (use Redis or database in production)
user_sessions = {}

def login_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        # Fixed: Verify session server-side
        session_id = session.get('session_id')
        if not session_id or session_id not in user_sessions:
            return redirect(url_for('login'))

        # Fixed: Get user info from server-side store
        user_data = user_sessions[session_id]
        if user_data.get('expired'):
            return redirect(url_for('login'))

        return f(user_data, *args, **kwargs)
    return decorated

def admin_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        session_id = session.get('session_id')
        if not session_id or session_id not in user_sessions:
            return redirect(url_for('login'))

        user_data = user_sessions[session_id]

        # Fixed: Role checked server-side, not from client
        if user_data.get('role') != 'admin':
            return "Access Denied", 403

        return f(user_data, *args, **kwargs)
    return decorated

@app.route('/transfer', methods=['POST'])
@login_required
def transfer_funds(user_data):
    # Fixed: Get user_id from server-side session, not form
    user_id = user_data['user_id']  # From authenticated session
    amount = float(request.form.get('amount'))
    target_account = request.form.get('target_account')

    # Now using verified user_id from server
    perform_transfer(user_id, target_account, amount)
    return "Transfer completed"

@app.route('/admin')
@admin_required
def admin_panel(user_data):
    # Fixed: Role verified server-side before reaching here
    return render_template('admin_panel.html')

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    user = authenticate_user(username, password)
    if user:
        # Fixed: Create server-side session
        session_id = secrets.token_urlsafe(32)
        user_sessions[session_id] = {
            'user_id': user.id,
            'role': user.role,  # Role from database
            'created_at': time.time()
        }
        session['session_id'] = session_id
        return redirect(url_for('dashboard'))

    return redirect(url_for('login', error='invalid'))

The fix stores authentication state server-side and only sends a cryptographically random session identifier to the client.


Exploited in the Wild

CVE-2002-1730 and CVE-2002-1734 documented web applications that granted administrative access when users set authentication cookies to "true", allowing trivial bypass of authentication.

Hidden Field Manipulation (E-Commerce, Ongoing)

Multiple e-commerce platforms have been exploited by modifying hidden form fields containing prices, quantities, or user IDs, allowing attackers to manipulate transactions.


Tools to Test/Exploit

  • Burp Suite — Web security tool for intercepting and modifying HTTP requests including cookies and hidden fields.

  • OWASP ZAP — Open-source web application security scanner with request interception capabilities.

  • Browser DevTools — Built-in browser tools for modifying cookies, forms, and headers.


CVE Examples

  • CVE-2002-1730 — Authentication bypass by setting cookie to "true".

  • CVE-2002-1734 — Authentication bypass via cookie modification.

  • CVE-2002-2064 — Admin access gained by setting a cookie value.

  • CVE-2005-1708 — Bypass through setting admin variable to true.

  • CVE-2005-1787 — Privilege escalation via hidden field manipulation.


References

  1. MITRE Corporation. "CWE-302: Authentication Bypass by Assumed-Immutable Data." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/302.html

  2. OWASP Foundation. "Broken Access Control." OWASP Top Ten. https://owasp.org/Top10/A01_2021-Broken_Access_Control/

  3. OWASP Foundation. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html