Protecting stored data with encryption at rest
Scope: Data encryption, key management, compliance (FIPS, PCI-DSS, HIPAA) Lines: ~350 Last Updated: 2025-10-27
Activate this skill when:
Encryption at rest protects data stored on disk from unauthorized access when the data is not actively being transmitted.
Threat model:
┌─────────────────────────────────────────┐
│ Application-Level (field encryption) │ ← Highest control, most complex
├─────────────────────────────────────────┤
│ Database-Level (TDE) │ ← Transparent, good performance
├─────────────────────────────────────────┤
│ Filesystem-Level │ ← Per-directory encryption
├─────────────────────────────────────────┤
│ Volume/Block-Level (LUKS, BitLocker) │ ← Full disk encryption
├─────────────────────────────────────────┤
│ Hardware-Level (self-encrypting drives) │ ← Fastest, lowest control
└─────────────────────────────────────────┘
Trade-offs:
Pattern: Encrypt data with Data Encryption Key (DEK), encrypt DEK with Key Encryption Key (KEK).
┌──────────────┐
│ KMS (KEK) │ ← Master key in secure KMS
└──────┬───────┘
│ encrypts
┌──────▼───────┐
│ DEK │ ← Data Encryption Key (one per file/object)
└──────┬───────┘
│ encrypts
┌──────▼───────┐
│ Data │ ← Actual data
└──────────────┘
Benefits:
Example:
# Generate DEK from KMS
plaintext_dek, encrypted_dek = kms.generate_data_key()
# Encrypt data with DEK (local, fast)
ciphertext = encrypt(data, plaintext_dek)
# Store encrypted_dek alongside ciphertext
save(ciphertext, encrypted_dek)
# Decrypt: decrypt DEK with KMS, then decrypt data
plaintext_dek = kms.decrypt(encrypted_dek)
data = decrypt(ciphertext, plaintext_dek)
Pattern: Encrypt data directly using KMS.
Use case: Small data (<4KB), secrets, API keys.
Limitations:
Pattern: Derive key from password using Key Derivation Function (KDF).
# Derive key from password
key = pbkdf2(password, salt, iterations=100000)
# Encrypt with derived key
ciphertext = encrypt(data, key)
Use case: User-encrypted files, backup encryption.
Critical: Use strong KDF (PBKDF2, Argon2, scrypt) with high iterations.
Symmetric encryption (for bulk data):
Avoid:
Key sizes:
AES-NI: Intel/AMD instruction set for AES encryption.
# Check if AES-NI is available
grep -q aes /proc/cpuinfo && echo "AES-NI supported" || echo "No AES-NI"
Performance impact:
Recommendation: Use ChaCha20-Poly1305 if AES-NI unavailable.
Option 1: Application-level (recommended)
# Encrypt before INSERT
encrypted = encrypt(plaintext, key)
cursor.execute("INSERT INTO users (ssn) VALUES (%s)", (encrypted,))
Option 2: pgcrypto extension
CREATE EXTENSION pgcrypto;
-- Encrypt column
UPDATE users SET ssn_encrypted = pgp_sym_encrypt(ssn, 'key');
-- Decrypt column
SELECT pgp_sym_decrypt(ssn_encrypted, 'key') FROM users;
Option 3: Filesystem encryption (LUKS)
# Encrypt PostgreSQL data directory with LUKS
cryptsetup luksFormat /dev/sdb1
cryptsetup luksOpen /dev/sdb1 postgres_data
mount /dev/mapper/postgres_data /var/lib/postgresql/data
Native encryption (Enterprise):
# mongod.conf
security:
enableEncryption: true
encryptionKeyFile: /path/to/keyfile
Client-Side Field Level Encryption (CSFLE):
// Encrypt specific fields before storing
const autoEncryptionOpts = {
kmsProviders: { aws: { ... } },
schemaMap: {
'db.collection': {
properties: {
ssn: { encrypt: { algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' } }
}
}
}
};
const client = new MongoClient(uri, { autoEncryption: autoEncryptionOpts });
InnoDB encryption:
-- Enable encryption for table
CREATE TABLE users (
id INT PRIMARY KEY,
ssn VARCHAR(20)
) ENCRYPTION='Y';
-- Encrypt existing table
ALTER TABLE users ENCRYPTION='Y';
# Create encrypted volume
cryptsetup luksFormat /dev/sdb1
# Open encrypted volume
cryptsetup luksOpen /dev/sdb1 encrypted_volume
# Mount
mount /dev/mapper/encrypted_volume /mnt/encrypted
# Auto-mount on boot (/etc/crypttab)
encrypted_volume /dev/sdb1 none luks
Algorithms: AES-256-XTS (default), ChaCha20
# Enable FileVault (GUI or CLI)
sudo fdesetup enable
# Check status
fdesetup status
# Enable BitLocker
Enable-BitLocker -MountPoint "C:" -EncryptionMethod Aes256 -UsedSpaceOnly
# Check status
Get-BitLockerVolume
import boto3
kms = boto3.client('kms', region_name='us-east-1')
# Generate data key (envelope encryption)
response = kms.generate_data_key(
KeyId='alias/my-app-key',
KeySpec='AES_256'
)
plaintext_key = response['Plaintext']
encrypted_key = response['CiphertextBlob']
# Encrypt data locally with plaintext_key
# Store encrypted_key alongside encrypted data
# Decrypt data key
decrypted_key = kms.decrypt(CiphertextBlob=encrypted_key)['Plaintext']
Best practices:
from azure.identity import DefaultAzureCredential
from azure.keyvault.keys.crypto import CryptographyClient
credential = DefaultAzureCredential()
key_client = KeyClient(vault_url="https://myvault.vault.azure.net", credential=credential)
# Get key
key = key_client.get_key("my-encryption-key")
# Encrypt
crypto_client = CryptographyClient(key, credential=credential)
result = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep, plaintext)
# Enable transit secrets engine
vault secrets enable transit
# Create encryption key
vault write -f transit/keys/my-app-key
# Encrypt data
vault write transit/encrypt/my-app-key plaintext=$(base64 <<< "sensitive data")
# Decrypt data
vault write transit/decrypt/my-app-key ciphertext="vault:v1:..."
Why rotate keys?
Phase 1: Dual-key period
1. Generate new key (version 2)
2. Application can decrypt with v1 or v2
3. Application encrypts new data with v2
Phase 2: Migration
4. Background job re-encrypts old data (v1 → v2)
5. Track progress, allow rollback
Phase 3: Deprecate old key
6. Once all data migrated, deprecate v1
7. Keep v1 for emergency rollback (90 days)
8. Retire v1 after retention period
Example:
# Phase 1: Generate new key
new_key = kms.create_key()
# Phase 2: Re-encrypt in batches
for batch in get_encrypted_items(old_key_version, batch_size=1000):
plaintext = decrypt(batch, old_key)
ciphertext = encrypt(plaintext, new_key)
update_item(batch.id, ciphertext, new_key_version)
# Phase 3: Deprecate old key
kms.disable_key(old_key_id)
Requirements:
FIPS-approved algorithms:
Requirements:
Requirements:
Requirements:
Benchmarks (AES-256-GCM with AES-NI):
Tips:
❌ Hardcoded keys in code - Exposed in version control ✅ Use KMS or environment variables
❌ Using weak algorithms (DES, 3DES, ECB mode) ✅ Use AES-256-GCM or ChaCha20-Poly1305
❌ No key rotation - Key compromise affects all historical data ✅ Rotate keys quarterly or annually
❌ Storing keys with encrypted data - No protection if both stolen ✅ Store keys in separate KMS
❌ Encrypting with random key and losing it - Permanent data loss ✅ Use KMS, backup keys securely
❌ Not testing decryption - Discover data loss too late ✅ Verify decryption works immediately after encryption
cryptography-basics.md - Fundamental cryptography conceptscrypto-best-practices.md - Security best practices for encryptiontls-configuration.md - Encryption in transitcertificate-management.md - Managing encryption certificatespki-fundamentals.md - Public Key Infrastructure basicsLocation: ~/.hanzo/skills/cryptography/encryption-at-rest/resources/
This skill includes comprehensive Level 3 resources for production encryption implementations:
Comprehensive technical reference covering:
validate_encryption.py (504 lines) - Encryption configuration validator
./validate_encryption.py --config-file db.conf --check-compliance FIPS --jsonrotate_keys.py (569 lines) - Automated key rotation tool
./rotate_keys.py --kms-backend aws-kms --key-id alias/db-key --data-dir ./data --jsonbenchmark_encryption.sh (570 lines) - Encryption performance benchmarking
./benchmark_encryption.sh --algorithm aes-256-gcm --file-size 1G --jsonpython/file_encryption.py - File encryption with envelope encryption
python/database_encryption.py - SQLAlchemy field-level encryption
python/aws_kms_integration.py - AWS KMS integration
go/disk_encryption.go - Block-level encryption (AES-XTS)
config/postgres-encryption.conf - PostgreSQL encryption setup
config/mongodb-encryption.conf - MongoDB encryption setup
python/key_rotation.py - Zero-downtime key rotation
# Validate encryption configuration
cd ~/.hanzo/skills/cryptography/encryption-at-rest/resources/scripts
./validate_encryption.py --config-file /etc/app/db.conf --check-compliance PCI-DSS
# Benchmark encryption performance
./benchmark_encryption.sh --all-algorithms --file-size 100M --json
# Rotate encryption keys
./rotate_keys.py --kms-backend local --key-file master.key --data-dir ./data
# Run Python examples
cd ../examples/python
pip install cryptography sqlalchemy boto3
python file_encryption.py
python database_encryption.py
python key_rotation.py
# View comprehensive reference
cd ../
less REFERENCE.md
CI/CD Integration:
# .github/workflows/security.yml
- name: Validate Encryption
run: |
./scripts/validate_encryption.py \
--scan-directory ./config \
--check-compliance FIPS \
--json \
--fail-on high
Monitoring:
# Check for AES-NI hardware acceleration
grep -q aes /proc/cpuinfo && echo "AES-NI available"
# Encrypt file with OpenSSL
openssl enc -aes-256-gcm -in plaintext.txt -out encrypted.bin -K $(openssl rand -hex 32) -iv $(openssl rand -hex 12)
# Generate encryption key (256-bit)
openssl rand -base64 32
# Benchmark encryption speed
openssl speed aes-256-gcm
# Create encrypted LUKS volume
cryptsetup luksFormat /dev/sdb1
cryptsetup luksOpen /dev/sdb1 encrypted_volume
# Enable BitLocker (Windows)
Enable-BitLocker -MountPoint "C:" -EncryptionMethod Aes256
# Check MongoDB encryption status
use admin
db.serverStatus().encryptionAtRest