Reliance on Cookies without Validation and Integrity Checking in a Security Decision
Description
Reliance on Cookies without Validation and Integrity Checking in a Security Decision is a web security vulnerability where software uses cookies for authentication, authorization, or other security decisions without properly validating the cookie's origin or integrity. Cookies are stored client-side and can be easily modified by users through browser developer tools, browser extensions, or by manipulating HTTP requests directly. When applications trust cookie values without verification, attackers can forge or manipulate cookies to bypass authentication, escalate privileges, or impersonate other users.
Risk
Cookie manipulation is trivial for attackers—cookies can be edited directly in browsers or through proxy tools. When security decisions rely on unvalidated cookies, attackers can set arbitrary values to bypass authentication (setting "authenticated=true"), escalate privileges (setting "role=admin"), or assume other users' identities. This vulnerability often leads to complete authentication bypass, as many flawed implementations check only for the presence or value of a cookie without verifying it was legitimately set by the server. The attack requires no special skills or tools, making it highly exploitable.
Solution
Never rely solely on cookie values for security decisions. Implement server-side session management where session identifiers map to server-stored session data. If cookies must carry security-relevant data, implement cryptographic integrity protection using HMACs or digital signatures. Use HttpOnly and Secure flags on sensitive cookies. Implement proper session management with server-side validation. For stateless designs, consider signed tokens (like JWTs) with proper signature verification. Include anti-replay mechanisms like timestamps or nonces. Validate that cookie values match expected formats and ranges on every request.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Bypass Protection Mechanism - Attackers manipulate cookies to bypass authentication or authorization checks. |
| Access Control | Scope: Access Control Gain Privileges or Assume Identity - Cookie manipulation enables privilege escalation or user impersonation. |
| Integrity | Scope: Integrity Modify Application Data - Forged cookies may affect application behavior and data integrity. |
Example Code
Vulnerable Code
// Vulnerable: Using cookie directly for authorization
import javax.servlet.http.*;
public class VulnerableServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String userRole = "guest";
// Vulnerable: Reading role directly from cookie without validation
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if (c.getName().equals("role")) {
userRole = c.getValue(); // Attacker sets: role=admin
}
}
}
// Vulnerable: Security decision based on unvalidated cookie
if (userRole.equals("admin")) {
showAdminPanel(response); // Attacker gains admin access!
} else {
showUserPanel(response);
}
}
}
// Vulnerable: Authentication bypass via cookie
<?php
$authenticated = $_COOKIE['authenticated'] ?? '0';
// Vulnerable: Trusting cookie for authentication status
if ($authenticated != '1') {
// User not authenticated, try to authenticate
if (authenticateUser($_POST['username'], $_POST['password']) === true) {
// Set cookie on successful authentication
setcookie("authenticated", "1", time() + 3600);
setcookie("username", $_POST['username'], time() + 3600);
} else {
die("Authentication failed");
}
} else {
// Vulnerable: Cookie says authenticated, so trust it
// Attacker just sets authenticated=1 cookie!
$username = $_COOKIE['username'];
echo "Welcome back, $username";
}
?>
# Vulnerable: Flask app using cookies for access control
from flask import Flask, request, make_response
app = Flask(__name__)
@app.route('/dashboard')
def vulnerable_dashboard():
# Vulnerable: Reading user ID from cookie
user_id = request.cookies.get('user_id')
is_admin = request.cookies.get('is_admin')
if not user_id:
return redirect('/login')
# Vulnerable: Using cookie values directly
user_data = get_user_data(user_id) # Attacker sets user_id to any user
# Vulnerable: Admin check based on cookie
if is_admin == 'true': # Attacker sets is_admin=true
return render_admin_dashboard(user_data)
return render_user_dashboard(user_data)
@app.route('/login', methods=['POST'])
def vulnerable_login():
username = request.form['username']
password = request.form['password']
if authenticate(username, password):
user = get_user(username)
response = make_response(redirect('/dashboard'))
# Vulnerable: Storing sensitive data in cookies
response.set_cookie('user_id', str(user.id))
response.set_cookie('is_admin', str(user.is_admin).lower())
return response
return "Login failed", 401
// Vulnerable: Node.js Express using cookies for auth
const express = require('express');
const app = express();
app.get('/admin', (req, res) => {
// Vulnerable: Trust cookie without verification
const adminToken = req.cookies.admin_token;
if (adminToken === 'valid_admin') { // Attacker sets admin_token=valid_admin
res.send('Admin Panel');
} else {
res.status(403).send('Access Denied');
}
});
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (authenticate(username, password)) {
const user = getUser(username);
// Vulnerable: Setting predictable cookie value
if (user.isAdmin) {
res.cookie('admin_token', 'valid_admin');
}
res.cookie('logged_in', 'true');
res.redirect('/dashboard');
}
});
Fixed Code
// Fixed: Server-side session management
import javax.servlet.http.*;
import java.security.SecureRandom;
import java.util.concurrent.ConcurrentHashMap;
public class FixedServlet extends HttpServlet {
// Server-side session storage
private static final ConcurrentHashMap<String, UserSession> sessions =
new ConcurrentHashMap<>();
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// Fixed: Use HttpSession (server-managed)
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect("/login");
return;
}
// Fixed: Get role from server-side session, not cookie
String userRole = (String) session.getAttribute("role");
if (userRole == null) {
userRole = "guest";
}
if ("admin".equals(userRole)) {
showAdminPanel(response);
} else {
showUserPanel(response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (authenticateUser(username, password)) {
HttpSession session = request.getSession(true);
User user = getUserByUsername(username);
// Fixed: Store role in server-side session
session.setAttribute("userId", user.getId());
session.setAttribute("role", user.getRole());
response.sendRedirect("/dashboard");
}
}
}
// Fixed: Server-side sessions with HMAC-protected cookies
<?php
session_start(); // Use PHP sessions instead of raw cookies
define('SECRET_KEY', getenv('SESSION_SECRET')); // From environment
function createSecureCookie($name, $value) {
$timestamp = time();
$data = json_encode(['value' => $value, 'timestamp' => $timestamp]);
$signature = hash_hmac('sha256', $data, SECRET_KEY);
$cookie_value = base64_encode($data) . '.' . $signature;
setcookie($name, $cookie_value, [
'expires' => time() + 3600,
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
}
function readSecureCookie($name) {
if (!isset($_COOKIE[$name])) return null;
$parts = explode('.', $_COOKIE[$name]);
if (count($parts) !== 2) return null;
list($data_b64, $signature) = $parts;
$data = base64_decode($data_b64);
// Fixed: Verify signature
$expected_sig = hash_hmac('sha256', $data, SECRET_KEY);
if (!hash_equals($expected_sig, $signature)) {
return null; // Tampered!
}
$parsed = json_decode($data, true);
// Fixed: Check timestamp to prevent replay
if (time() - $parsed['timestamp'] > 3600) {
return null; // Expired
}
return $parsed['value'];
}
// Fixed: Use server-side session
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
if (authenticateUser($_POST['username'], $_POST['password'])) {
$_SESSION['authenticated'] = true;
$_SESSION['user_id'] = getUserId($_POST['username']);
$_SESSION['role'] = getUserRole($_POST['username']);
} else {
die("Authentication failed");
}
}
// Fixed: Read from server session, not cookie
$user_id = $_SESSION['user_id'];
$role = $_SESSION['role'];
?>
# Fixed: Flask with server-side sessions and signed cookies
from flask import Flask, request, session, redirect
from flask_session import Session
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(32) # Strong secret key
# Use server-side sessions
app.config['SESSION_TYPE'] = 'redis'
Session(app)
@app.route('/dashboard')
def fixed_dashboard():
# Fixed: Check server-side session
if 'user_id' not in session:
return redirect('/login')
# Fixed: Get user data from database using session user_id
user = get_user_by_id(session['user_id'])
if not user:
session.clear()
return redirect('/login')
# Fixed: Check role from database, not session/cookie
if user.is_admin:
return render_admin_dashboard(user)
return render_user_dashboard(user)
@app.route('/login', methods=['POST'])
def fixed_login():
username = request.form['username']
password = request.form['password']
if authenticate(username, password):
user = get_user(username)
# Fixed: Store only user_id in session
session['user_id'] = user.id
session['login_time'] = time.time()
# Don't store is_admin - check from DB each time
return redirect('/dashboard')
return "Login failed", 401
CVE Examples
- CVE-2009-1549: Authentication bypass by setting cookies to specific hardcoded values.
- CVE-2009-1619: Admin privileges gained by setting "admin" cookie to value "1".
- CVE-2009-0864: CMS admin panel access by setting "login" cookie to "OK".
- CVE-2008-5784: Dating application admin access via admin cookie set to "1".
- CVE-2008-6291: Email manager admin access by setting login cookie to "admin".
References
- MITRE Corporation. "CWE-784: Reliance on Cookies without Validation and Integrity Checking in a Security Decision." https://cwe.mitre.org/data/definitions/784.html
- OWASP. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
- OWASP. "Testing for Cookies Attributes." OWASP Testing Guide.