Database TDE or column-level encryption for sensitive fields
Why it matters
CWE-311 and CWE-312 describe the same failure at two layers: storing sensitive data without encryption (CWE-311) and storing passwords or credentials in a recoverable format (CWE-312). When neither TDE nor column-level encryption is present, every backup file, DB replica, and read replica contains plaintext cardholder data. PCI-DSS 4.0 Req-3.4 requires protection of stored cardholder data; NIST SC-28 requires protecting the confidentiality of data at rest. A breach that dumps the database — via SQL injection, misconfigured IAM, or insider access — becomes a full cardholder data exposure with no additional attacker steps. OWASP 2021 A02 (Cryptographic Failures) specifically calls out missing database encryption as a root cause.
Severity rationale
High because absence of TDE or column encryption means any database read path — including replicas, snapshots, and developer copies — exposes sensitive data without additional exploitation steps.
Remediation
Enable TDE at the cloud provider level first (it covers every table automatically), then add application-level column encryption for the highest-sensitivity fields. In Prisma, annotate encrypted columns so they're never queried or returned raw:
// prisma/schema.prisma — document encryption expectations in comments
model Account {
id String @id @default(cuid())
// Encrypted with AES-256-GCM before insert; decrypted on read in AccountService
cardToken String @db.Text
// Encrypted with AES-256-GCM; never selected in list queries
ssn String? @db.Text
balance Decimal @db.Decimal(15, 2)
}
For PostgreSQL, enable encryption at rest in AWS RDS by checking "Enable encryption" when creating the instance, or use pgcrypto for column-level encryption on an existing unencrypted instance.
Detection
- ID:
tde-or-column-encryption - Severity:
high - What to look for: Count all database tables that store sensitive data and enumerate which ones have TDE or column-level encryption. Quote the actual TDE setting or encryption function found. Check cloud database provider settings and application code for encryption patterns in schema migrations or ORM definitions.
- Pass criteria: Database has TDE enabled covering at least 1 sensitive table, OR at least 90% of sensitive columns have application-level encryption verified in schema files or ORM definitions. Report the count even on pass (e.g., "TDE enabled on PostgreSQL, 4 of 4 sensitive tables covered").
- Fail criteria: Database has no TDE enabled and no column-level encryption detected — 0 encryption mechanisms found for sensitive data.
- Skip (N/A) when: Project stores no financial data in its own database (purely external payment processor delegation — cite the actual processor found).
- Detail on fail:
"0 of 3 sensitive tables have encryption — no TDE, no column-level encryption in application code" - Remediation:
- For PostgreSQL/MySQL: Enable encryption at rest via your database provider's console (AWS RDS, Supabase, etc.).
- For Application-Level: Define encrypted columns in your schema migrations:
// Prisma example model Account { id String @id @default(cuid()) cardNumber String @db.VarChar(255) // Application-encrypted before insert balance Decimal @db.Decimal(15, 2) // Application-encrypted before insert }
External references
- cwe · CWE-311 — Missing Encryption of Sensitive Data
- cwe · CWE-312 — Cleartext Storage of Sensitive Information
- owasp:2021 · A02 — Cryptographic Failures
- nist:rev5 · SC-28 — Protection of Information at Rest
- pci-dss:4.0 · Req-3.4 — Render PAN unreadable anywhere stored
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-encryption·automated