Cleartext Storage of Sensitive Information in a Cookie
Description
Cleartext Storage of Sensitive Information in a Cookie is a vulnerability that occurs when a product stores sensitive information in cleartext within browser cookies. Cookies are stored on the client's computer and transmitted with each HTTP request, making them accessible to users, browser extensions, and potentially attackers through XSS vulnerabilities or physical access to the device. Sensitive information stored in cleartext cookies can be easily read and potentially modified. Even encoded data (such as Base64) provides no real security, as attackers can easily determine the encoding method and decode the information.
Risk
Cleartext cookie storage exposes sensitive data through multiple attack vectors. Cross-site scripting (XSS) vulnerabilities allow attackers to steal cookies remotely. Physical access to a device allows direct reading of cookie files. Browser extensions can access cookie data. Network-level attackers can intercept cookies transmitted over unencrypted HTTP connections. Man-in-the-middle attacks can capture cookies even when the initial connection uses HTTPS if cookie security flags are not set. The risk is amplified because cookies persist across browser sessions and may remain accessible even after users log out if not properly expired. Exposed credentials enable account takeover, while exposed session data can enable session hijacking.
Solution
Never store sensitive information directly in cookies. For authentication, use server-side sessions with only a secure, random session identifier in the cookie. If cookies must contain data, encrypt it server-side using authenticated encryption before storage and decrypt only on the server. Set the HttpOnly flag to prevent JavaScript access and mitigate XSS-based cookie theft. Set the Secure flag to ensure cookies are only transmitted over HTTPS. Use the SameSite attribute to prevent cross-site request forgery. Implement short cookie expiration times for sensitive data. Store sensitive user data server-side and reference it via session identifiers rather than including it in cookies. Never store credentials, financial data, or personal information in cookies.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Attackers can read sensitive information stored in cookies through various methods including XSS attacks, physical access, browser extensions, or network interception. |
| Authentication, Access Control | Scope: Authentication, Access Control Exposed credentials enable account takeover. Exposed session data enables session hijacking. Attackers can impersonate users and access protected resources. |
Example Code
Vulnerable Code (Java/PHP)
The following examples demonstrate cleartext cookie storage vulnerabilities:
// Vulnerable: Storing account ID in plaintext cookie
import javax.servlet.http.*;
public class VulnerableCookieStorage extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
String accountId = request.getParameter("account");
// Vulnerable: Plaintext account ID in cookie
Cookie cookie = new Cookie("userAccountID", accountId);
cookie.setMaxAge(3600); // 1 hour
response.addCookie(cookie);
// Attacker can read and modify account ID!
}
// Vulnerable: Storing credentials in cookie
public void persistLogin(HttpServletResponse response,
String username, String password) {
// Vulnerable: Plaintext username in cookie
Cookie userCookie = new Cookie("username", username);
response.addCookie(userCookie);
// Extremely Vulnerable: Plaintext password in cookie!
Cookie passCookie = new Cookie("password", password);
response.addCookie(passCookie);
// No HttpOnly, Secure, or SameSite flags!
}
// Vulnerable: Storing role/permissions in cookie
public void setUserRole(HttpServletResponse response, String role) {
// Vulnerable: User can modify to gain admin access
Cookie roleCookie = new Cookie("userRole", role);
response.addCookie(roleCookie);
// Attacker changes cookie to "admin" and gains privileges!
}
}
<?php
// Vulnerable: Storing login credentials in plaintext cookie
function persistLogin($username, $password) {
// Vulnerable: Plaintext credentials in cookie
$data = array("username" => $username, "password" => $password);
setcookie("userdata", json_encode($data), time() + 3600);
// Anyone with cookie access gets credentials!
}
// Vulnerable: Storing sensitive user data in cookie
function setUserSession($userData) {
// Vulnerable: PII in plaintext cookie
$sessionData = array(
"user_id" => $userData['id'],
"email" => $userData['email'],
"ssn" => $userData['ssn'], // SSN in cookie!
"credit_card" => $userData['card'] // Credit card in cookie!
);
setcookie("session", base64_encode(json_encode($sessionData)));
// Base64 is NOT encryption - easily decoded!
}
// Vulnerable: Storing authentication token without security flags
function setAuthToken($token) {
// Vulnerable: Missing security flags
setcookie(
"auth_token",
$token,
time() + 86400,
"/"
// Missing: secure, httponly, samesite flags!
);
}
?>
# Vulnerable: Flask cleartext cookie storage
from flask import Flask, request, make_response
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(redirect('/dashboard'))
# Vulnerable: Plaintext credentials in cookies
response.set_cookie('username', username)
response.set_cookie('user_password', password) # Password in cookie!
# Vulnerable: Sensitive data without security flags
response.set_cookie('user_id', str(get_user_id(username)))
response.set_cookie('user_role', get_user_role(username))
return response
return redirect('/login?error=1')
Fixed Code (Java/PHP)
// Fixed: Secure session handling without sensitive cookie data
import javax.servlet.http.*;
import javax.crypto.*;
import java.security.SecureRandom;
import java.util.Base64;
public class SecureCookieStorage extends HttpServlet {
private final SecretKey encryptionKey;
private final Map<String, SessionData> serverSessions = new ConcurrentHashMap<>();
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
String username = request.getParameter("username");
// Fixed: Create server-side session
String sessionId = generateSecureSessionId();
serverSessions.put(sessionId, new SessionData(username));
// Fixed: Only store session ID in cookie, not account data
Cookie sessionCookie = new Cookie("session_id", sessionId);
sessionCookie.setHttpOnly(true); // Fixed: No JavaScript access
sessionCookie.setSecure(true); // Fixed: HTTPS only
sessionCookie.setMaxAge(3600);
sessionCookie.setPath("/");
// Fixed: Set SameSite attribute (requires Servlet 4.0+ or header)
response.setHeader("Set-Cookie",
sessionCookie.getName() + "=" + sessionCookie.getValue() +
"; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600");
response.addCookie(sessionCookie);
}
// Fixed: Server-side session lookup
public SessionData getSession(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies == null) return null;
for (Cookie cookie : cookies) {
if ("session_id".equals(cookie.getName())) {
return serverSessions.get(cookie.getValue());
}
}
return null;
}
// Fixed: If cookie data needed, encrypt it
public void setEncryptedCookie(HttpServletResponse response,
String name, String value) {
String encrypted = encrypt(value);
Cookie cookie = new Cookie(name, encrypted);
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setMaxAge(3600);
response.addCookie(cookie);
}
private String encrypt(String data) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
SecureRandom.getInstanceStrong().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(128, iv));
byte[] encrypted = cipher.doFinal(data.getBytes());
byte[] combined = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
return Base64.getUrlEncoder().encodeToString(combined);
} catch (Exception e) {
throw new RuntimeException("Encryption failed", e);
}
}
private String generateSecureSessionId() {
byte[] bytes = new byte[32];
new SecureRandom().nextBytes(bytes);
return Base64.getUrlEncoder().encodeToString(bytes);
}
}
<?php
// Fixed: Secure session handling
function secureLogin($userId) {
// Fixed: Use PHP sessions instead of credential cookies
session_start();
session_regenerate_id(true); // Prevent fixation
// Fixed: Store user reference in server-side session
$_SESSION['user_id'] = $userId;
$_SESSION['created_at'] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
// Fixed: Configure secure session cookie
$cookieParams = [
'lifetime' => 3600,
'path' => '/',
'secure' => true, // HTTPS only
'httponly' => true, // No JavaScript access
'samesite' => 'Strict' // CSRF protection
];
session_set_cookie_params($cookieParams);
// No sensitive data in cookies!
}
// Fixed: Encrypted cookie if data must be stored client-side
function setEncryptedCookie($name, $data) {
$key = getEncryptionKey(); // Retrieve from secure storage
$iv = random_bytes(16);
$encrypted = openssl_encrypt(
json_encode($data),
'AES-256-GCM',
$key,
0,
$iv,
$tag
);
$cookieValue = base64_encode($iv . $tag . $encrypted);
setcookie(
$name,
$cookieValue,
[
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]
);
}
function getEncryptedCookie($name) {
if (!isset($_COOKIE[$name])) {
return null;
}
$key = getEncryptionKey();
$data = base64_decode($_COOKIE[$name]);
$iv = substr($data, 0, 16);
$tag = substr($data, 16, 16);
$encrypted = substr($data, 32);
$decrypted = openssl_decrypt(
$encrypted,
'AES-256-GCM',
$key,
0,
$iv,
$tag
);
return json_decode($decrypted, true);
}
?>
# Fixed: Secure Flask session handling
from flask import Flask, session, redirect, request, make_response
from flask_session import Session
import secrets
app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_bytes(32)
app.config['SESSION_TYPE'] = 'redis' # Server-side session
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
Session(app)
@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')
if authenticate(username, password):
# Fixed: Server-side session storage
session.regenerate() # Prevent fixation
session['user_id'] = get_user_id(username)
session['username'] = username
session['authenticated'] = True
session['created_at'] = time.time()
# Fixed: No sensitive data in cookies
# Only session ID is transmitted
return redirect('/dashboard')
return redirect('/login?error=1')
@app.route('/dashboard')
def dashboard():
# Fixed: Verify session server-side
if not session.get('authenticated'):
return redirect('/login')
# Get user data from server-side session
user_id = session.get('user_id')
return render_template('dashboard.html', user=get_user(user_id))
The fix uses server-side sessions with secure session cookies and proper security flags.
Exploited in the Wild
Admin Password Cookie Exposure (Web Applications, 2002)
CVE-2002-1800 documented a web application storing the administrator password in a cleartext cookie, enabling complete administrative access theft.
Default Configuration Credential Exposure (Enterprise Software, 2001)
CVE-2001-1537 and CVE-2001-1536 documented products with default configurations that stored usernames and passwords in cleartext cookies.
Tools to Test/Exploit
-
Browser Developer Tools — Built-in tools for viewing and modifying cookies.
-
Burp Suite — Web security tool for intercepting and analyzing cookies.
-
OWASP ZAP — Open-source scanner that identifies insecure cookie settings.
CVE Examples
-
CVE-2002-1800 — Admin password stored in cleartext cookie.
-
CVE-2001-1537 — Cleartext usernames/passwords in cookies.
-
CVE-2001-1536 — Usernames/passwords in cleartext cookies.
-
CVE-2005-2160 — Authentication information stored unencrypted in cookie.
References
-
MITRE Corporation. "CWE-315: Cleartext Storage of Sensitive Information in a Cookie." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/315.html
-
OWASP Foundation. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
-
RFC 6265. "HTTP State Management Mechanism." https://tools.ietf.org/html/rfc6265