Weakness reference
CWE-539

Use of Persistent Cookies Containing Sensitive Information

This weakness occurs when an application stores sensitive data such as authentication tokens, API keys, or personal information in a persistent cookie that…

01Summary

This weakness occurs when an application stores sensitive data (such as authentication tokens, API keys, or personal information) in a persistent cookie that does not expire or expires far in the future. Because persistent cookies remain on a user's device indefinitely, they create an extended window of exposure: if a device is compromised, stolen, or accessed by an unauthorized person, the sensitive data can be extracted and misused long after the user's session should have ended.

02How It Happens

Applications sometimes use persistent cookies for convenience—to keep users logged in across browser restarts or to avoid repeated authentication. However, when sensitive information is stored in these cookies without appropriate expiration, the application violates the principle of least privilege and data minimization. The vulnerability arises from a combination of two mistakes: storing sensitive data in a cookie (rather than server-side session storage) and failing to set a short, appropriate expiration time. Even if the cookie is marked as secure and HTTP-only, a persistent cookie with no expiration date remains accessible to attackers who gain physical or logical access to the device.

03Real-World Impact

An attacker who obtains a persistent cookie containing an authentication token or API key can impersonate the legitimate user indefinitely, or until the token is manually revoked—which may never happen if the user is unaware of the compromise. This can lead to unauthorized access to accounts, data theft, fraudulent transactions, or lateral movement within connected systems. The longer the cookie persists, the greater the window of opportunity for exploitation and the lower the likelihood that the compromise will be detected in time.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request, make_response
from datetime import datetime, timedelta

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')
    
    # Authenticate user (simplified)
    if authenticate(username, password):
        response = make_response("Login successful")
        # Persistent cookie with no expiration — remains for years
        response.set_cookie('auth_token', 'sensitive_token_value', 
                           max_age=None)  # No expiration
        return response
    return "Login failed", 401

Why it's vulnerable:
The cookie is set with max_age=None, meaning it persists indefinitely on the user's device. If the device is compromised, the token can be extracted and reused without time constraint.

Fixed pattern
from flask import Flask, request, make_response
from datetime import datetime, timedelta

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')
    
    if authenticate(username, password):
        response = make_response("Login successful")
        # Short-lived session cookie; expires in 1 hour
        response.set_cookie('session_id', generate_session_id(),
                           max_age=3600,  # 1 hour
                           secure=True,
                           httponly=True,
                           samesite='Strict')
        return response
    return "Login failed", 401
Vulnerable pattern
<?php
// User logs in
if (authenticate($_POST['username'], $_POST['password'])) {
    // Persistent cookie — no expiration set
    setcookie('user_token', 'sensitive_token_value');
    echo "Login successful";
} else {
    echo "Login failed";
}
?>

Why it's vulnerable:
The setcookie() call omits the expiration parameter, creating a persistent cookie that survives browser restarts and remains accessible if the device is compromised.

Fixed pattern
<?php
// User logs in
if (authenticate($_POST['username'], $_POST['password'])) {
    // Session cookie expires in 1 hour
    $expiration = time() + 3600;
    setcookie('session_id', generate_session_id(),
              [
                  'expires' => $expiration,
                  'path' => '/',
                  'secure' => true,
                  'httponly' => true,
                  'samesite' => 'Strict'
              ]);
    echo "Login successful";
} else {
    echo "Login failed";
}
?>

05Prevention Checklist

Use short expiration times:
Set cookie max_age or expires to a reasonable session duration (typically 15 minutes to 1 hour), not indefinite or years-long.
Store sensitive data server-side:
Keep authentication tokens, API keys, and personal information in server-side session storage (e.g., Redis, database) rather than in cookies.
Use session cookies when possible:
Omit the expiration parameter entirely to create session cookies that expire when the browser closes, rather than persistent cookies.
Mark cookies as secure and HTTP-only:
Always set Secure, HttpOnly, and SameSite flags to limit exposure even if a cookie is compromised.
Implement token rotation:
Periodically issue new session tokens and invalidate old ones, reducing the window of exposure if a token is leaked.
Audit existing cookies:
Review your application's cookie usage to identify any persistent cookies storing sensitive data and migrate them to short-lived alternatives.

06Signs You May Already Be Affected

Review your application's cookie-setting code and browser developer tools for cookies with no expiration date or expiration dates far in the future (e.g., year 2099). Check server logs for unusual access patterns or token reuse from different IP addresses or devices, which may indicate a stolen persistent cookie. If you find authentication tokens or API keys in cookies that persist across browser restarts, that is a direct indicator of this weakness.

07Related Recent Vulnerabilities