Missing Encryption of Sensitive Data
Description
Missing Encryption of Sensitive Data occurs when software does not encrypt sensitive or critical information before storage or transmission. This exposes data to unauthorized parties who can intercept network traffic, access storage media, or exploit other vulnerabilities. Sensitive data includes passwords, financial information, personal health information, session tokens, and private keys. Without encryption, this data is readable by anyone who gains access to it.
Risk
Unencrypted sensitive data is a critical vulnerability. Data breaches exposing unencrypted databases have affected billions of users. Network interception (man-in-the-middle attacks) can capture unencrypted credentials in transit. Physical access to storage media exposes unencrypted data at rest. Compliance frameworks (PCI-DSS, HIPAA, GDPR) require encryption of sensitive data, and violations result in significant fines.
Solution
Encrypt all sensitive data at rest using AES-256 or similar strong algorithms. Use TLS 1.3 for data in transit. Implement proper key management—never hardcode keys. Use dedicated encryption libraries, not custom implementations. Encrypt database columns containing PII or credentials. Use full-disk encryption for storage media. Implement envelope encryption for cloud environments. Consider data classification to identify what needs encryption.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Data Exposure Unencrypted data can be read by anyone with access to storage or network traffic. |
| Compliance | Scope: Regulatory Violations Storing PII, financial, or health data unencrypted violates GDPR, PCI-DSS, HIPAA. |
| Integrity | Scope: Data Tampering Unencrypted data can be modified without detection. |
Example Code + Solution Code
Vulnerable Code
# VULNERABLE: Passwords stored in plaintext
class User(db.Model):
username = db.Column(db.String(80))
password = db.Column(db.String(120)) # Plaintext!
def create_user(username, password):
user = User(username=username, password=password)
db.session.add(user)
db.session.commit()
# VULNERABLE: Sensitive data in plaintext file
def save_api_keys(keys):
with open('api_keys.txt', 'w') as f:
for key in keys:
f.write(f"{key}\n") # Plaintext!
# VULNERABLE: HTTP instead of HTTPS
import requests
def send_credentials(username, password):
# Sending over unencrypted HTTP!
requests.post('http://api.example.com/login', json={
'username': username,
'password': password
})
# VULNERABLE: Unencrypted database connection
def connect_database():
return psycopg2.connect(
host='db.example.com',
database='production',
user='app',
password='secret'
# No SSL/TLS!
)
// VULNERABLE: Plaintext sensitive data storage
public class UserService {
public void saveUser(User user) {
// Password stored as plaintext
String sql = "INSERT INTO users (username, password, ssn) VALUES (?, ?, ?)";
jdbcTemplate.update(sql,
user.getUsername(),
user.getPassword(), // Plaintext!
user.getSsn() // PII unencrypted!
);
}
}
// VULNERABLE: Unencrypted file storage
public class DataExporter {
public void exportUserData(List<User> users) throws IOException {
try (FileWriter writer = new FileWriter("users.csv")) {
for (User user : users) {
// PII written in plaintext
writer.write(user.getName() + "," +
user.getSsn() + "," +
user.getCreditCard() + "\n");
}
}
}
}
// VULNERABLE: Unencrypted config
@Configuration
public class AppConfig {
// Sensitive data in plaintext config
@Value("${db.password}") // From plaintext application.properties
private String dbPassword;
}
// VULNERABLE: Plaintext data in localStorage
function saveUserData(user) {
// Sensitive data stored unencrypted in browser
localStorage.setItem('user_session', JSON.stringify({
userId: user.id,
token: user.token,
creditCard: user.creditCard // Plaintext!
}));
}
// VULNERABLE: Unencrypted API communication
async function submitPayment(cardNumber, cvv, amount) {
// Sending payment data over HTTP
const response = await fetch('http://api.example.com/payment', {
method: 'POST',
body: JSON.stringify({ cardNumber, cvv, amount })
});
return response.json();
}
Fixed Code
# SAFE: Properly hashed passwords and encrypted data
from argon2 import PasswordHasher
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os
import base64
ph = PasswordHasher()
class User(db.Model):
username = db.Column(db.String(80))
password_hash = db.Column(db.String(256)) # Hashed!
def create_user(username, password):
# Hash password with Argon2
password_hash = ph.hash(password)
user = User(username=username, password_hash=password_hash)
db.session.add(user)
db.session.commit()
def verify_password(stored_hash, password):
try:
ph.verify(stored_hash, password)
return True
except:
return False
# SAFE: Encrypted file storage
def get_encryption_key():
# Key from environment or key management system
key = os.environ.get('ENCRYPTION_KEY')
return base64.urlsafe_b64decode(key)
def save_api_keys_encrypted(keys):
fernet = Fernet(get_encryption_key())
encrypted_data = []
for key in keys:
encrypted_data.append(fernet.encrypt(key.encode()))
with open('api_keys.enc', 'wb') as f:
for encrypted_key in encrypted_data:
f.write(encrypted_key + b'\n')
def load_api_keys_encrypted():
fernet = Fernet(get_encryption_key())
with open('api_keys.enc', 'rb') as f:
encrypted_keys = f.read().strip().split(b'\n')
return [fernet.decrypt(key).decode() for key in encrypted_keys]
# SAFE: HTTPS only
import requests
def send_credentials_secure(username, password):
# Using HTTPS
response = requests.post('https://api.example.com/login',
json={'username': username, 'password': password},
verify=True # Verify SSL certificate
)
return response
# SAFE: Encrypted database connection
def connect_database_secure():
return psycopg2.connect(
host='db.example.com',
database='production',
user='app',
password=os.environ['DB_PASSWORD'],
sslmode='verify-full', # Require SSL
sslrootcert='/path/to/ca-cert.pem'
)
// SAFE: Encrypted sensitive data
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
@Service
public class SecureUserService {
private final Argon2PasswordEncoder passwordEncoder = new Argon2PasswordEncoder();
private final EncryptionService encryptionService;
public void saveUser(User user) {
// Hash password
String passwordHash = passwordEncoder.encode(user.getPassword());
// Encrypt PII
String encryptedSsn = encryptionService.encrypt(user.getSsn());
String sql = "INSERT INTO users (username, password_hash, ssn_encrypted) VALUES (?, ?, ?)";
jdbcTemplate.update(sql, user.getUsername(), passwordHash, encryptedSsn);
}
}
@Service
public class EncryptionService {
private SecretKey key; // Loaded from secure key management
public String encrypt(String plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = generateSecureIV();
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// Prepend IV to ciphertext
byte[] combined = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(combined);
}
public String decrypt(String encryptedData) throws Exception {
byte[] combined = Base64.getDecoder().decode(encryptedData);
byte[] iv = Arrays.copyOfRange(combined, 0, 12);
byte[] ciphertext = Arrays.copyOfRange(combined, 12, combined.length);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
}
}
// SAFE: Encrypted configuration
@Configuration
public class SecureAppConfig {
// Using encrypted properties or secrets manager
@Value("${db.password}") // Jasypt encrypted or from Vault
private String dbPassword;
// Or use AWS Secrets Manager
@Bean
public String getDbPassword() {
return secretsManager.getSecretValue("db-password");
}
}
// SAFE: Encrypted browser storage
const crypto = require('crypto-js');
function saveUserDataEncrypted(user, encryptionKey) {
// Only store essential non-sensitive data
const sessionData = {
userId: user.id,
token: user.token
// Don't store credit card!
};
// Encrypt before storing
const encrypted = crypto.AES.encrypt(
JSON.stringify(sessionData),
encryptionKey
).toString();
sessionStorage.setItem('user_session', encrypted); // Use sessionStorage, not localStorage
}
function loadUserDataEncrypted(encryptionKey) {
const encrypted = sessionStorage.getItem('user_session');
if (!encrypted) return null;
const decrypted = crypto.AES.decrypt(encrypted, encryptionKey);
return JSON.parse(decrypted.toString(crypto.enc.Utf8));
}
// SAFE: HTTPS only for sensitive data
async function submitPaymentSecure(cardNumber, cvv, amount) {
// Tokenize instead of sending raw card data
const token = await tokenizeCard(cardNumber, cvv);
// Only HTTPS
const response = await fetch('https://api.example.com/payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Strict-Transport-Security': 'max-age=31536000'
},
body: JSON.stringify({ paymentToken: token, amount })
});
return response.json();
}
Exploited in the Wild
Equifax Data Breach (2017)
The Equifax breach exposed 147 million records. While the initial vector was a web vulnerability, the impact was worsened by sensitive data stored without adequate encryption, including SSNs in plaintext.
Adobe Password Breach (2013)
Adobe suffered a breach exposing 153 million user records. Passwords were encrypted with 3DES-ECB (not hashed), allowing attackers to crack millions of passwords through pattern analysis.
Marriott Data Breach (2018)
Marriott's Starwood reservation system breach exposed 383 million guest records, including unencrypted passport numbers for 5.25 million guests.
Tools to test/exploit
-
Wireshark — analyze network traffic for unencrypted data.
-
Burp Suite — intercept and analyze HTTP traffic.
-
SQLite Browser — examine unencrypted local databases.
-
testssl.sh — test TLS configuration.
CVE Examples
-
CVE-2019-1010238 — GNOME Gnome-keyring weak encryption.
-
CVE-2021-22893 — Pulse Secure sensitive data exposure.
-
CVE-2020-8945 — GPG sensitive data in plaintext.
References
-
MITRE. "CWE-311: Missing Encryption of Sensitive Data." https://cwe.mitre.org/data/definitions/311.html
-
OWASP. "Cryptographic Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html