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
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Attackers can bypass authentication entirely by modifying data assumed to be immutable, gaining unauthorized access as any user including administrators. |
| Integrity, Confidentiality | Scope: 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
Cookie-Based Admin Bypass (Web Applications, 2002)
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
-
MITRE Corporation. "CWE-302: Authentication Bypass by Assumed-Immutable Data." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/302.html
-
OWASP Foundation. "Broken Access Control." OWASP Top Ten. https://owasp.org/Top10/A01_2021-Broken_Access_Control/
-
OWASP Foundation. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html