CWE-311: Missing Encryption of Sensitive Data
Overview
Missing encryption occurs when sensitive or security-critical data is stored or transmitted without appropriate cryptographic protection, exposing it to unauthorized access, interception, or tampering.
Relationship to Other CWEs
MITRE marks CWE-311 DISCOURAGED for mapping: it is "high-level with more specific children available", and MITRE names both of them - CWE-312 for unencrypted storage, CWE-319 for unencrypted transmission. Report a finding against whichever of those it is. This page is the right level only when the finding is that a piece of sensitive data has no cryptographic protection anywhere, rather than that one named store or one named channel is in cleartext.
The pages around it differ by where the unprotected data sits, and by whether the encryption is missing or merely bad:
- CWE-311 (this page) - sensitive data is left unprotected as a design decision, covering storage and transmission together
- CWE-312 (Cleartext Storage of Sensitive Information) - the data-at-rest half: databases, files, logs, caches, backups, and memory
- CWE-319 (Cleartext Transmission of Sensitive Information) - the data-in-transit half: anything crossing a network channel that is not encrypted
- CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) - a MITRE peer, where encryption is present but the algorithm no longer protects the data. The remediation differs: this page is about adding protection, CWE-327 about replacing it
OWASP Classification
A06:2025 - Insecure Design
Risk
High: Anyone who reaches the network path or the storage can read or modify sensitive data that is not encrypted, and where the data is regulated the exposure is a compliance failure as well as a breach.
Remediation Steps
Core Principle: Encrypt sensitive data before storage or transmission when it crosses a trust boundary or could be exposed through another weakness.
One exception, and it is the most common finding of all: stored passwords are not an encryption problem. Encryption is reversible by design, which is exactly what a password store must not be - a key compromise or an insider with decrypt access returns every password in cleartext, and the credentials are reusable on other sites. Passwords are stored as the output of an adaptive password hash with a per-password salt: Argon2id or scrypt, which are memory-hard as well as slow, or bcrypt where neither is available - it is CPU-hard but not memory-hard, and OWASP scopes it to legacy systems. See CWE-256 for storing them in plaintext, CWE-261 for a reversible encoding standing in for a hash, and CWE-916 for using a hash that is too weak or too fast. Everything below about keys, rotation and KMS applies to data you need to read back, not to passwords, and reaching for AES because a scanner said "encryption" is how a password store ends up decryptable.
Two neighbouring cases do need encryption rather than hashing, because the application has to recover the value: credentials the application itself presents to a downstream system (a database or SMTP password held in configuration), and API keys or tokens it must replay. Those belong in a secret manager or an encrypted store with the key held separately.
Locate the missing encryption
- Start from the file and line the finding points at, and identify what sensitive data is unprotected: PII, financial data, session tokens, API keys, or credentials the application must present to other systems
- Separate user passwords out of that list before going further - they need hashing, not encryption, as above
- Work out whether the data is in transit over a network or at rest in storage, a database, or files
- Work out who can reach the unencrypted data: an attacker on the network path, anyone with file-system access, or whoever holds a database dump
Identify where encryption is required (Primary Defense)
- Private keys, payment data, regulated personal data, session tokens, health records, confidential business data, and credentials the application must be able to read back all need encryption. User passwords need password hashing instead - the distinction is whether anything legitimately needs the original value.
- Data at rest and data in transit are separate cases: CWE-312 covers storage and CWE-319 covers transmission.
- Decide where encryption is needed by asking who can access the storage, channel, backup, logs, cache, or intermediate system.
- The strongest protection for data that does not need to persist is not storing it.
- Database, cloud-storage, KMS, service-mesh, and platform TLS controls reduce implementation mistakes when configured correctly, so prefer them over application code where they fit.
Encrypt sensitive data in transit
- Use TLS 1.2+ for all network communications: HTTPS for web traffic, TLS for database connections, secure WebSocket (
wss://) - Enforce HTTPS for web applications: Redirect all HTTP to HTTPS with a 301, and send the HSTS header, Strict-Transport-Security: max-age=31536000
- Encrypt internal communications: Use TLS for service-to-service calls and message queues, and mTLS where both ends must authenticate
- Secure database connections: PostgreSQL
sslmode=verify-fullwhere possible, MySQLrequire_secure_transport=ON, MongoDBtls=true
Encrypt sensitive data at rest
- Use authenticated encryption: AES-GCM, ChaCha20-Poly1305, or another vetted AEAD mode for new application-level encryption
- Encrypt database fields: Use application-level encryption (encrypt before INSERT) where the field must survive a database dump or a compromised database credential; Transparent Data Encryption protects the stored files from a stolen disk or medium and decrypts for any authorized connection, so the two are complements rather than alternatives
- Encrypt files containing sensitive data: Config files with credentials, backup files, log files with PII
- Use full-disk encryption as defense-in-depth: Enable BitLocker on Windows, FileVault on macOS, or LUKS on Linux - it protects a drive that leaves the building, not data on a running system
- Encrypt cloud storage: Use server-side encryption (S3 SSE-KMS, Azure Storage Encryption) or client-side encryption
Implement secure key management
- Protect encryption keys: Use key management systems (AWS KMS, Azure Key Vault, HashiCorp Vault), never hardcode keys in source code
- Use vetted cryptographic libraries: OpenSSL, Java JCA, .NET System.Security.Cryptography, Python cryptography, Node.js crypto
- Avoid weak or custom cryptography: Don't use DES, 3DES, RC4, or homegrown algorithms
- Rotate encryption keys: Implement rotation policies appropriate to the data and system, and rotate immediately after suspected key exposure
- Separate keys from data: Store them in a different system or service from what they encrypt
Monitor and audit encryption usage
- Regularly review encryption configurations (TLS versions, cipher suites, key strengths)
- Log encryption failures or suspicious access (decryption errors, key access logs)
- Scan codebase for unencrypted sensitive data transmission (grep for 'http://', plaintext passwords)
- Monitor certificate expiration and renewal (SSL certificate monitoring tools)
- Track compliance with encryption policies (PCI DSS, HIPAA, GDPR requirements)
Test the encryption implementation
- Verify sensitive data is encrypted in transit, for example by checking protocol configuration and using packet capture in a controlled test environment
- Verify sensitive data is encrypted at rest by inspecting the database and files for ciphertext
- Test with security scanning tools (SSL Labs for HTTPS configuration, SAST scanners for code)
- Verify decryption works correctly for legitimate users
- Re-scan with security scanner to confirm the issue is resolved
Common Vulnerable Patterns
- Transmitting sensitive data over HTTP or unencrypted channels
- Storing PII, payment data or tokens in plaintext
- Storing user passwords in any recoverable form, encrypted included
- Using weak or deprecated algorithms (e.g., DES, RC4)
Unencrypted Transmission of Sensitive Data
// VULNERABLE - pseudo-code
http_post("http://example.com/api", sensitive_data) // plaintext scheme
// Attack: anyone on the network path reads the payload as it passes
Why this is vulnerable: Anyone positioned on the path reads the payload, and "the path" is longer than it looks - a hop inside a cloud provider, a container network shared with other workloads, a corporate proxy, an ISP. Cleartext also has no integrity, so the same position allows modifying the request or the response, which is often the more damaging half.
Two follow-on points decide whether a finding is really closed. TLS terminating at a load balancer protects only the segment in front of it, so the leg from there to the application is a separate decision that is frequently left plaintext on the grounds that it is internal. And a client that requests http:// and is redirected to https:// has already sent the first request in the clear - the redirect closes nothing for the request that triggered it. HSTS narrows the window rather than removing it: the header only takes effect once a client has received one successful HTTPS response, so the very first contact from a given browser is still made in the open. Submitting the domain to the HSTS preload list is what closes that case, and it requires includeSubDomains and preload on the header as well as a commitment to keep the domain on HTTPS.
Secure Patterns
HTTPS Encryption for Data in Transit
// SECURE - pseudo-code
http_post("https://example.com/api", sensitive_data,
verify_certificate = true) // the default in a high-level HTTP client
// Additionally: enforce TLS 1.2 or later and reject weak ciphers at the server
Why this works: TLS encrypts the request in transit, so an attacker on the network path can neither read the payload nor modify it. Certificate verification ties the connection to the legitimate server rather than an impersonator. TLS 1.2 or later provides strong ciphers such as AES-GCM and forward secrecy.
The verification default is a property of the client you called, not of the platform. The maintained HTTP clients - requests, HttpClient, net/http, fetch - validate the chain and the hostname unless something turns it off. The TLS APIs beneath them often do not: an SSLSocket from Java's default SSLSocketFactory completes a handshake against the wrong host, and Python's SSLContext.wrap_socket() checks the name only when it is given server_hostname. Hand-rolled LDAPS, SMTP and message-queue clients live at that layer, so a transport that is encrypted there is not necessarily authenticated. CWE-295 covers what to check and how each runtime spells it.