Skip to content

CWE-539: Use of Persistent Cookies Containing Sensitive Information - Remediation

In short CWE-539 is sensitive data, such as PII, stored in persistent cookies, which are saved across sessions. Prevent it by omitting sensitive data from cookies, masking it, and encrypting it before storage.

CWE-539: Use of Persistent Cookies Containing Sensitive Information is a security vulnerability that involves the storage of sensitive data, such as Personally Identifiable Information (PII), in persistent cookies. These cookies, which are saved across sessions, can pose significant security risks if not handled properly. This article details the risks associated with PII exposure through CWE-539 and outlines remediation strategies, supported by coding examples in Java, JavaScript, and Python.

  1. Unauthorized Access: Persistent cookies can be intercepted or accessed by unauthorized parties, especially if stored in plaintext.

  2. Data Theft: If cookies containing PII are compromised, it can lead to identity theft and fraud.

  3. Session Hijacking: Persistent cookies often store session information, which can be used for session hijacking if compromised.

  1. Reputational Damage: Breaches involving PII can damage an organization’s reputation, leading to a loss of customer trust and potential customer attrition.

  2. Legal and Compliance Issues: Non-compliance with data protection laws (like GDPR and CCPA) due to improper handling of cookies can result in hefty fines and legal actions.

  3. Operational Disruptions: Responding to and recovering from data breaches can consume significant resources and time.

To mitigate the risks associated with storing sensitive information in persistent cookies, organizations should implement strategies like omitting sensitive data, masking data, and encrypting data before storage.

Avoid storing sensitive or personally identifiable information in cookies altogether.

Java
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
public class CookieManager {
public void createSessionCookie(HttpServletResponse response, String sessionId) {
Cookie sessionCookie = new Cookie("sessionID", sessionId);
sessionCookie.setMaxAge(24 * 60 * 60); // Set for 24 hours
response.addCookie(sessionCookie);
// Ensure that no sensitive PII is stored in cookies
}
}
Javascript
// Express: HttpOnly cookies can only be set server-side, never via document.cookie
function createSessionCookie(res, sessionId) {
res.cookie('sessionID', sessionId, { httpOnly: true, secure: true, sameSite: 'strict', path: '/', maxAge: 86400000 });
// Do not store sensitive PII in cookies
}
Python
from http.cookies import SimpleCookie
def create_session_cookie(session_id):
cookie = SimpleCookie()
cookie["sessionID"] = session_id
cookie["sessionID"]["max-age"] = 86400 # Set for 24 hours
cookie["sessionID"]["httponly"] = True
print(cookie.output())
# Sensitive PII is not stored in the cookie

If any data must be stored in cookies and has potential privacy implications, it should be masked or anonymized.

Java
import org.apache.commons.codec.digest.DigestUtils;
public class DataMasker {
// PEPPER is loaded from your secret store, never hardcoded
private static final String PEPPER = System.getenv("USER_ID_PEPPER");
public String maskUserId(String userId) {
return "user-" + DigestUtils.sha256Hex(PEPPER + userId).substring(0, 12);
}
}
Javascript
const crypto = require('crypto');
// PEPPER is loaded from your secret store, never hardcoded
const PEPPER = process.env.USER_ID_PEPPER;
function maskUserId(userId) {
return `user-${crypto.createHash('sha256').update(PEPPER + userId).digest('hex').slice(0, 12)}`;
}
Python
import hashlib
import os
# PEPPER is loaded from your secret store, never hardcoded
PEPPER = os.environ['USER_ID_PEPPER']
def mask_user_id(user_id):
return 'user-' + hashlib.sha256((PEPPER + user_id).encode()).hexdigest()[:12]

Encrypt any sensitive information before storing it in a cookie to protect it from unauthorized access.

Java
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
public class EncryptionUtility {
// key is supplied by the caller, sourced from your KMS or secret store
public static void encryptAndAddCookie(HttpServletResponse response, String data, SecretKey key) throws Exception {
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
byte[] payload = ByteBuffer.allocate(iv.length + encryptedData.length).put(iv).put(encryptedData).array();
String encryptedString = java.util.Base64.getEncoder().encodeToString(payload);
Cookie secureCookie = new Cookie("secureData", encryptedString);
secureCookie.setHttpOnly(true);
secureCookie.setMaxAge(24 * 60 * 60);
response.addCookie(secureCookie);
}
}
Javascript
const crypto = require('crypto');
// Express: the cookie is set server-side so HttpOnly is actually honored
// key: 32-byte Buffer loaded from your KMS or secret store
function encryptAndAddCookie(res, data, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const encrypted = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]);
const value = iv.toString('hex') + ':' + encrypted.toString('hex');
res.cookie('secureData', value, { httpOnly: true, secure: true, sameSite: 'strict', path: '/', maxAge: 86400000 });
}
Python
from cryptography.fernet import Fernet
from http.cookies import SimpleCookie
# key: Fernet key loaded from your secret store, never generated per call
def encrypt_and_add_cookie(data, key):
cipher_suite = Fernet(key)
encrypted_data = cipher_suite.encrypt(data.encode())
cookie = SimpleCookie()
cookie["secureData"] = encrypted_data.decode()
cookie["secureData"]["max-age"] = 86400
cookie["secureData"]["httponly"] = True
print(cookie.output())

HoundDog.ai’s Privacy Code Scanner (hounddog scan) reports CWE-539, together with CWE-315, when unsanitized sensitive data reaches the Cookies sink. It identifies sensitive data elements in source code and traces them through assignments, transformations, string interpolations, and function calls to the sinks they reach.

  • Sensitive data types. The scanner ships with over 100 data element definitions covering common PII, PHI, CHD, PIFI, and secret data (tags PII, PHI, CHD, PIFI, SECRET); the full list is in the scanner’s data-elements.md. A finding names the data element involved, such as Email or Social Security Number.

  • Sinks. CWE-315 and CWE-539 are both reported on the Cookies data sink, which is risky by default. By default the scanner allowlists auth-token, email, username, and password for this sink, so those data elements in cookies are not flagged; remove entries from the allowlist under Scanner Rules if your policy is stricter.

  • Languages. Dataflows are reported for Python, JavaScript, TypeScript, Java, C#/.NET, and Go. Other supported languages are scanned for data elements only and produce no dataflows. See Supported Languages.

  • Sanitizers. If the data passes through a recognized sanitizer (masking, hashing, encryption, redaction) before it reaches the sink, the dataflow is treated as safe and is not flagged. Organizations can add their own patterns under Sanitizers.

For example, scanning a TypeScript file that writes a Social Security Number to a cookie with the js-cookie library produces a finding like this (console output, excerpt; the file path is shortened):

Cookies.set("profile", user.ssn);
🟥 CRITICAL: Social Security Number sent to Cookies in /path/to/project/app.ts:4:3
4 │ Cookies.set("profile", user.ssn)
Associated Compliance Frameworks
────────────────────────────────
CWE: CWE-315, CWE-539
OWASP: A04:2021
Privacy: CCPA, GDPR-A5-32, NIST-800-53

To run a scan, see the quickstart. For how data elements, data sinks, and sanitizers are defined and customized, see the scanner rules reference.

Mitigating the risks associated with CWE-539 is essential for protecting PII and maintaining the security of web applications. By implementing effective cookie management strategies such as omitting sensitive data, masking, and encrypting information, organizations can enhance their compliance with privacy regulations and secure the data of their users.