CWE-312: Cleartext Storage of Sensitive Information - Remediation Guide
In short CWE-312 is sensitive information, such as PII, stored in cleartext in places like databases, configuration files, or logs. Prevent it by omitting sensitive data that is not needed, masking values, and encrypting whatever must be stored.
Introduction
Section titled “Introduction”CWE-312: Cleartext Storage of Sensitive Information occurs when an application stores sensitive information like Personally Identifiable Information (PII) in cleartext. This storage can be in various locations such as databases, configuration files, or logs. Storing sensitive data without proper encryption exposes it to various risks, making it a critical security flaw to address. This article discusses the risks associated with PII exposure due to CWE-312 and provides coding examples in Java, JavaScript, and Python to demonstrate remediation techniques.
Understanding the Risks
Section titled “Understanding the Risks”Direct Risks
Section titled “Direct Risks”-
Data Theft: Unauthorized access to storage systems can lead to direct theft of PII.
-
Identity Theft: Exposed PII can be used to impersonate individuals.
-
Financial Fraud: Sensitive data like credit card details or bank account information can be used for fraudulent transactions.
Indirect Risks
Section titled “Indirect Risks”-
Reputational Damage: Incidents of data exposure can lead to loss of customer trust and damage to the company’s reputation.
-
Legal and Regulatory Penalties: Failure to protect data can result in fines and sanctions under laws like GDPR, HIPAA, or CCPA.
-
Remediation Costs: The cost of addressing a data breach, including incident response and increased security measures, can be substantial.
Remediation Techniques
Section titled “Remediation Techniques”To mitigate CWE-312, organizations should ensure sensitive data is never stored in cleartext. Techniques include omitting unnecessary sensitive data, masking data to hide true values, and encrypting data to protect its integrity and confidentiality.
1. Omitting Data
Section titled “1. Omitting Data”Omit sensitive data when it’s not necessary for the application’s functionality.
public class User { private String username; // Omit storing sensitive PII such as social security numbers unless absolutely necessary}function createUser(username) { // Only store essential information; omit sensitive PII like address or phone number return { username };}class User: def __init__(self, username): self.username = username # Sensitive PII such as date of birth is omitted from storage2. Masking Data
Section titled “2. Masking Data”When storing data that might identify individuals, mask parts of the data.
public String maskEmail(String email) { int index = email.indexOf('@'); String localPart = email.substring(0, index); return localPart.replaceAll(".", "*") + email.substring(index);}function maskEmail(email) { let [local, domain] = email.split('@'); local = local.replace(/./g, '*'); return `${local}@${domain}`;}def mask_email(email): local, domain = email.split('@') masked_local = '*' * len(local) return f'{masked_local}@{domain}'3. Encrypting Data
Section titled “3. Encrypting Data”Encrypt sensitive data before storage to ensure it is protected from unauthorized access.
import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.spec.GCMParameterSpec;import java.nio.ByteBuffer;import java.nio.charset.StandardCharsets;import java.security.SecureRandom;
public class EncryptionUtility { // secretKey is supplied by the caller, sourced from your KMS or secret store public static String encrypt(String data, SecretKey secretKey) throws Exception { byte[] iv = new byte[12]; new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, 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(); return java.util.Base64.getEncoder().encodeToString(payload); }}const crypto = require('crypto');
// key: 32-byte Buffer loaded from your KMS or secret storefunction encrypt(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()]); return iv.toString('hex') + ':' + encrypted.toString('hex');}from cryptography.fernet import Fernet
# key: Fernet key loaded from your secret store, never generated per calldef encrypt(data, key): cipher = Fernet(key) encrypted_data = cipher.encrypt(data.encode()) return encrypted_data.decode()Detecting this with HoundDog.ai
Section titled “Detecting this with HoundDog.ai”HoundDog.ai’s Privacy Code Scanner (hounddog scan) reports CWE-312 when unsanitized sensitive data reaches a storage
sink marked risky. 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-312 is reported on storage-related sinks. The browser Local Storage and Session Storage sinks (
localStorage.setItemand its session counterpart) are risky by default. Database, file, configuration file, and cloud storage sinks, such as PostgreSQL, MySQL, AWS S3, and Files, are marked safe by default: dataflows into them are recorded and appear in the Datamap but are not flagged as risky, unless you set the sink to risky under Scanner Rules. -
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.
-
Sanitization. When AI dataflow reviews are enabled, each dataflow is reviewed with its code and trace in context, including any sanitization logic (masking, hashing, encryption, redaction) applied before the data reaches the sink. Dataflows where the data is effectively sanitized are marked as false positives.
For example, scanning a TypeScript file that writes a Social Security Number to local storage produces a finding like this (console output, excerpt; the file path is shortened):
localStorage.setItem("profile", user.ssn);🟥 CRITICAL: Social Security Number sent to Local Storage in /path/to/project/app.ts:2:3
2 │ localStorage.setItem("profile", user.ssn)
Associated Compliance Frameworks────────────────────────────────CWE: CWE-312OWASP: A04:2021Privacy: CCPA, GDPR-A5-32, NIST-800-53To run a scan, see the quickstart. For how data elements and data sinks are defined and customized, see the scanner rules reference.
Conclusion
Section titled “Conclusion”Implementing these remediation strategies is crucial for protecting PII from exposure through cleartext storage (CWE-312). Properly handling sensitive information not only prevents data breaches but also ensures compliance with various data protection regulations, thereby maintaining trust and safeguarding against potential financial and reputational harm.
