# Database encryption is enabled for sensitive data at rest

- **Pattern:** `ab-001178` (`ecommerce-pci.cardholder-data.database-encryption`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001178
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001178)

## Why it matters

PCI-DSS 4.0 Req 3.5 mandates encryption of stored account data; Req 3.3 prohibits storage of sensitive authentication data post-authorization. A database without encryption at rest means any storage-layer compromise — a stolen disk, a snapshot shared to the wrong account, or a cloud misconfiguration — exposes cardholder data in plaintext. NIST SP 800-53 SC-28 and OWASP A02 (Cryptographic Failures) both treat unencrypted data stores as high-severity findings. For managed services like RDS or Supabase, missing `storage_encrypted = true` means the infrastructure configuration actively opts out of protection that is trivially available.

## Severity rationale

Critical because an unencrypted database volume means a storage-layer breach yields plaintext cardholder data with no decryption barrier, satisfying PCI-DSS Req 3.5 as a direct violation.

## Remediation

Enable encryption at rest before the database instance is created — this cannot be changed on a running RDS instance without a snapshot-and-restore cycle. Store the encryption key in a managed key service, never in the codebase. Reference `terraform/main.tf` for infrastructure changes.

```hcl
resource "aws_db_instance" "main" {
  storage_encrypted = true
  kms_key_id        = aws_kms_key.db.arn
}
```

For Supabase, encryption at rest is managed automatically but verify it in the project dashboard under Settings > Infrastructure. For self-managed PostgreSQL, add column-level encryption via `pgcrypto` as a supplementary control where field-level sensitivity is highest.

## Detection

- **ID:** `database-encryption`
- **Severity:** `critical`
- **What to look for:** Count all database instances referenced in infrastructure code (Terraform, CloudFormation, docker-compose, Kubernetes manifests). For each instance, check for encryption-at-rest configuration. For managed services (RDS, Cloud SQL, Azure SQL, Supabase), look for `storage_encrypted = true` or equivalent in service configs. For self-managed databases, look for Transparent Data Encryption (TDE), file-level encryption, or encrypted volume configs. Count the number of encryption key references (KMS, vault, etc.).
- **Pass criteria:** Database encryption at rest is enabled on at least 1 database instance. For managed services, `storage_encrypted` or equivalent is set to `true` in infrastructure code. For self-managed databases, encryption is enabled and keys are stored separately from the database (at least 1 KMS or key management reference found). Report the count: X of Y database instances have encryption enabled.
- **Fail criteria:** Database has no encryption at rest enabled, or encryption is disabled explicitly (e.g., `storage_encrypted = false`). Do NOT pass when encryption is mentioned only in comments or documentation but not in actual infrastructure configuration.
- **Skip (N/A) when:** No database instances found in the project (no database dependencies in `package.json`, no ORM config, no database connection strings).
- **Detail on fail:** Specify the database type and missing encryption. Example: `"PostgreSQL database configured without Transparent Data Encryption (TDE). Encryption at rest not enabled. 0 of 1 instances encrypted."` or `"RDS instance created with StorageEncrypted: false in terraform/main.tf line 42"`
- **Cross-reference:** See `ecommerce-pci.cardholder-data.cde-isolated` (CDE isolation), `ecommerce-pci.cardholder-data.no-default-credentials` (credential management).
- **Remediation:** Enable encryption at rest on your database. For Supabase/PostgreSQL:

  ```sql
  -- Supabase manages TDE automatically, but verify in dashboard
  -- For self-managed PostgreSQL with pgcrypto
  CREATE EXTENSION IF NOT EXISTS pgcrypto;
  CREATE TABLE sensitive_data (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    encrypted_value BYTEA,
    created_at TIMESTAMP DEFAULT NOW()
  );

  -- For AWS RDS
  -- Enable in Terraform:
  resource "aws_db_instance" "main" {
    storage_encrypted = true
    kms_key_id        = aws_kms_key.db.arn
  }
  ```

  Verify encryption is enabled in your database service dashboard. Store encryption keys in a key management service (AWS KMS, Azure Key Vault, Google Cloud KMS), never in your codebase.

## External references

- pci-dss Req 3.5
- pci-dss Req 3.3
- cwe CWE-312
- owasp A02
- nist SC-28

Taxons: cryptography-and-secrets, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-001178
