What Is Encryption at Rest?
Encryption at rest protects data stored on disk or in databases by making it unreadable without the correct decryption key. If an attacker gains physical access to storage media or bypasses access controls, encrypted data remains confidential. This is a fundamental security control required by compliance frameworks including PCI DSS, HIPAA, SOC 2, and GDPR.
Encryption Layers
| Layer | What It Protects | When to Use |
|-------|------------------|-------------|
| Disk encryption | Entire storage volume | Always |
| Database encryption | Specific tables or columns | Sensitive PII data |
| File-level encryption | Individual files | Shared storage, backups |
| Application-level encryption | Specific data fields | End-to-end data protection |
| Backup encryption | Backup archives | All offsite storage |
Disk-Level Encryption
LUKS (Linux Unified Key Setup)
# Encrypt a disk with LUKS
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 encrypted_volume
sudo mkfs.ext4 /dev/mapper/encrypted_volume
sudo mount /dev/mapper/encrypted_volume /mnt/secure
AWS EBS Encryption
# Enable default EBS encryption
aws ec2 enable-ebs-encryption-by-default --region us-east-1
# Create an encrypted volume with a custom KMS key
aws ec2 create-volume \
--size 100 \
--region us-east-1 \
--availability-zone us-east-1a \
--encrypted \
--kms-key-id alias/my-app-key
GCE Persistent Disk Encryption
# Google Cloud uses AES-256 by default (CSEK for customer-managed)
# Create and apply a CSEK
gcloud compute disks create secure-disk \
--size 100GB \
--zone us-central1-a \
--csek-key-file ./key.json
gcloud compute instances attach-disk my-instance \
--disk secure-disk \
--zone us-central1-a \
--csek-key-file ./key.json
Database Encryption
Transparent Data Encryption (TDE)
-- PostgreSQL with pg_tde extension
CREATE EXTENSION pg_tde;
-- Initialize encryption key
SELECT pg_tde_add_key_provider_file('file-vault', '/etc/postgresql/key.file');
SELECT pg_tde_set_key('my-db-key', 'file-vault');
-- Encrypt a table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT,
ssn TEXT
) USING TDE;
-- Or encrypt existing table
ALTER TABLE users SET ACCESS METHOD TDE;
Column-Level Encryption
-- PostgreSQL with pgcrypto
CREATE EXTENSION pgcrypto;
-- Encrypt specific columns
CREATE TABLE patients (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
ssn BYTEA, -- Encrypted
diagnosis BYTEA, -- Encrypted
created_at TIMESTAMP DEFAULT NOW()
);
-- Insert with encryption
INSERT INTO patients (name, ssn, diagnosis)
VALUES (
'John Doe',
pgp_sym_encrypt('123-45-6789', 'encryption-key'),
pgp_sym_encrypt('Diabetes Type 2', 'encryption-key')
);
-- Decrypt when needed (with proper access control)
SELECT
name,
pgp_sym_decrypt(ssn, 'encryption-key') AS ssn
FROM patients
WHERE id = 1;
MongoDB Field-Level Encryption
const client = new MongoClient(uri, {
autoEncryption: {
keyVaultNamespace: 'encryption.__keyVault',
kmsProviders: {
aws: {
accessKeyId: process.env.AWS_ACCESS_KEY,
secretAccessKey: process.env.AWS_SECRET_KEY
}
},
schemaMap: {
'test.users': {
bsonType: 'object',
encryptMetadata: {
keyId: [UUID('...')],
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
},
properties: {
ssn: { encrypt: { bsonType: 'string' } },
creditCard: { encrypt: { bsonType: 'string' } }
}
}
}
}
});
Application-Level Encryption
For end-to-end protection where even the database administrator should not see plaintext:
from cryptography.fernet import Fernet
class FieldEncryptor:
def __init__(self, key):
self.cipher = Fernet(key)
def encrypt_field(self, plaintext):
"""Encrypt a single field."""
return self.cipher.encrypt(plaintext.encode())
def decrypt_field(self, ciphertext):
"""Decrypt a single field."""
return self.cipher.decrypt(ciphertext).decode()
# Usage
key = Fernet.generate_key() # Store this securely in a vault
encryptor = FieldEncryptor(key)
user_data = {
'email': 'user@example.com',
'ssn': encryptor.encrypt_field('123-45-6789'),
'medical_record': encryptor.encrypt_field('Sensitive diagnosis data')
}
# Store user_data in database
db.users.insert_one(user_data)
Key Management
Key Hierarchy
Master Key (HSM or KMS)
|
├── Key Encryption Key (KEK)
│ └── Data Encryption Keys (DEK)
└── Key Encryption Key (KEK)
└── Data Encryption Keys (DEK)
AWS KMS
import boto3
kms = boto3.client('kms')
def create_encryption_key():
response = kms.create_key(
Description='Application data encryption key',
KeyUsage='ENCRYPT_DECRYPT',
CustomerMasterKeySpec='SYMMETRIC_DEFAULT'
)
return response['KeyMetadata']['KeyId']
def encrypt_data(plaintext, key_id):
response = kms.encrypt(
KeyId=key_id,
Plaintext=plaintext.encode()
)
return response['CiphertextBlob']
def decrypt_data(ciphertext):
response = kms.decrypt(CiphertextBlob=ciphertext)
return response['Plaintext'].decode()
Key Rotation
| Key Type | Rotation Frequency | Method |
|----------|-------------------|--------|
| Master key | Annually (manual) | Create new, re-wrap DEKs |
| Data encryption key | Per-encryption | Generate new per operation |
| Database password | 90 days | Automated via secrets manager |
| TLS certificate | 90 days | cert-manager / ACM |
Compliance Requirements
| Standard | Encryption Requirement |
|----------|----------------------|
| PCI DSS 4.0 | All cardholder data encrypted at rest |
| HIPAA | ePHI encrypted at rest |
| SOC 2 | Encryption controls for sensitive data |
| GDPR | Appropriate technical measures (encryption) |
| FedRAMP | FIPS 140-2 validated encryption |
Summary
Encryption at rest is a non-negotiable security control for any application handling sensitive data. Implement defense in depth: disk encryption for the entire volume, transparent database encryption for tables, column-level encryption for the most sensitive fields, and application-level encryption for end-to-end protection. Use AWS KMS or similar managed services for key management with automatic rotation, and always encrypt backups before storing them offsite.