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

ImpactDetails
Access ControlScope: Access Control

Bypass Protection Mechanism - Attackers manipulate cookies to bypass authentication or authorization checks.
Access ControlScope: Access Control

Gain Privileges or Assume Identity - Cookie manipulation enables privilege escalation or user impersonation.
IntegrityScope: 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

  1. MITRE Corporation. "CWE-784: Reliance on Cookies without Validation and Integrity Checking in a Security Decision." https://cwe.mitre.org/data/definitions/784.html
  2. OWASP. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
  3. OWASP. "Testing for Cookies Attributes." OWASP Testing Guide.