Database Encryption
Database Encryption Layers
Database encryption protects data at rest and in transit. Multiple layers provide defense in depth.
Transparent Data Encryption (TDE)
TDE encrypts the entire database at the storage layer:
-- SQL Server TDE
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER CERTIFICATE DatabaseCert;
ALTER DATABASE ProductionDB
SET ENCRYPTION ON;
-- Check encryption status
SELECT DB_NAME(database_id) as DatabaseName,
encryption_state_desc,
percent_complete
FROM sys.dm_database_encryption_keys;
-- PostgreSQL TDE (with pg_tde extension)
CREATE EXTENSION pg_tde;
SELECT pg_tde_add_database_key_provider(
'file-vault',
'{"type":"file","path":"/etc/postgresql/keys.json"}'
);
SELECT pg_tde_set_principal_key('production-db-key', 'file-vault');
Column-Level Encryption
Encrypt specific sensitive columns:
from cryptography.fernet import Fernet
import base64
class ColumnEncryptor:
def __init__(self, master_key):
self.fernet = Fernet(master_key)
def encrypt_column(self, value):
if value is None:
return None
return self.fernet.encrypt(value.encode()).decode()
def decrypt_column(self, encrypted_value):
if encrypted_value is None:
return None
return self.fernet.decrypt(encrypted_value.encode()).decode()
def searchable_encryption(self, value):
"""Deterministic encryption for searchable columns"""
from cryptography.hazmat.primitives import hashes
digest = hashes.Hash(hashes.SHA256())
digest.update(value.encode())
digest.update(b"deterministic_salt")
return digest.finalize().hex()[:32]
# Usage
encryptor = ColumnEncryptor(os.environ["COLUMN_ENCRYPTION_KEY"])
# Encrypt PII before storage
cursor.execute("""
INSERT INTO users (email, ssn, name)
VALUES (%s, %s, %s)
""", (
encryptor.encrypt_column("alice@example.com"),
encryptor.encrypt_column("123-45-6789"),
"Alice"
))
Application-Level Encryption
Encrypt data before it reaches the database:
// Node.js application-level encryption
const crypto = require("crypto");
class ApplicationEncryptor {
constructor(encryptionKey) {
this.algorithm = "aes-256-gcm";
this.key = crypto.scryptSync(encryptionKey, "salt", 32);
}
encrypt(plaintext) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
let encrypted = cipher.update(plaintext, "utf8", "hex");
encrypted += cipher.final("hex");
const authTag = cipher.getAuthTag().toString("hex");
return JSON.stringify({
iv: iv.toString("hex"),
data: encrypted,
tag: authTag,
version: 1
});
}
decrypt(encrypted) {
const { iv, data, tag } = JSON.parse(encrypted);
const decipher = crypto.createDecipheriv(
this.algorithm,
this.key,
Buffer.from(iv, "hex")
);
decipher.setAuthTag(Buffer.from(tag, "hex"));
let decrypted = decipher.update(data, "hex", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
}
Key Management
key_management:
key_hierarchy:
master_key: aws_kms / hsm
database_key: encrypted_by_master_key
column_keys: encrypted_by_database_key
rotation:
master_key: yearly
database_key: monthly
column_keys: on_demand
access_control:
- kms:key_owner
- kms:encrypt
- kms:decrypt
audit:
- all_key_usage_logged
- key_access_alerting
- unauthorized_access_blocked
Performance Considerations
-- Benchmark: Column-level vs TDE
-- TDE overhead: 1-3% performance impact
-- Column-level overhead: 5-15% per encrypted column
-- Indexes on encrypted columns
-- Deterministic encryption allows exact match
-- Order-preserving encryption allows range queries
-- Homomorphic encryption allows computations (slow)
CREATE INDEX idx_email_hash
ON users (sha2(email, 256)); -- Searchable hash
Conclusion
Layer your database encryption strategy. Use TDE for bulk encryption with minimal performance impact. Use column-level encryption for specific PII fields. Use application-level encryption for the strongest protection. Manage keys carefully with a hierarchical key system and rotate regularly. Always encrypt data in transit with TLS.