Executive Summary & GDPR Context
Article 32 of the General Data Protection Regulation (GDPR) mandates “Security of Processing,” explicitly calling out pseudonymisation and encryption of personal data as state-of-the-art technical measures. Furthermore, Article 25 (Data Protection by Design and by Default) requires engineers to embed encryption primitives directly into the architecture rather than relying on perimeter security alone.
This guide provides concrete, copy-pasteable implementation patterns for:
- Encryption at Rest: Field-level encryption (FLE) using AES-256-GCM with authenticated key rotation.
- Encryption in Transit: Strict TLS 1.3 server configuration and HTTP Strict Transport Security (HSTS) header enforcement.
1. Field-Level Encryption at Rest (AES-256-GCM)
While disk encryption (LUKS, AWS EBS Encryption) protects against physical theft of hard drives, it does not protect against SQL injection, database compromise, or rogue internal database administrators. Field-Level Encryption (FLE) ensures that sensitive PII (Personally Identifiable Information)—such as national IDs, emails, and financial metrics—remains encrypted inside database buffers and tables.
Key Architecture: AES-256-GCM with Envelope Encryption
- Algorithm: AES-256-GCM (Galois/Counter Mode) providing authenticated encryption with associated data (AEAD).
- IV Generation: Cryptographically secure 12-byte (96-bit) Initialization Vector per record. Never reuse an IV with the same key.
- Auth Tag: 16-byte (128-bit) authentication tag to detect data tampering before decryption.
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
interface EncryptedPayload {
ciphertext: string; // Hex-encoded cipher text
iv: string; // Hex-encoded initialization vector
tag: string; // Hex-encoded authentication tag
keyVersion: number; // Master key version for zero-downtime rotation
}
export class FieldEncryptionService {
private masterKeys: Map<number, Buffer>;
private activeVersion: number;
constructor(keys: Record<number, string>, activeVersion: number) {
this.masterKeys = new Map();
for (const [v, hexKey] of Object.entries(keys)) {
const keyBuf = Buffer.from(hexKey, 'hex');
if (keyBuf.length !== 32) {
throw new Error(`Key version ${v} must be exactly 256 bits (32 bytes).`);
}
this.masterKeys.set(Number(v), keyBuf);
}
this.activeVersion = activeVersion;
}
/**
* Encrypts plaintext string using AES-256-GCM
*/
public encrypt(plaintext: string): EncryptedPayload {
const iv = randomBytes(12); // 96-bit IV for GCM
const key = this.masterKeys.get(this.activeVersion);
if (!key) throw new Error(`Active key version ${this.activeVersion} missing`);
const cipher = createCipheriv('aes-256-gcm', key, iv);
let ciphertext = cipher.update(plaintext, 'utf8', 'hex');
ciphertext += cipher.final('hex');
const tag = cipher.getAuthTag().toString('hex');
return {
ciphertext,
iv: iv.toString('hex'),
tag,
keyVersion: this.activeVersion,
};
}
/**
* Decrypts payload and verifies authenticity tag
*/
public decrypt(payload: EncryptedPayload): string {
const key = this.masterKeys.get(payload.keyVersion);
if (!key) throw new Error(`Key version ${payload.keyVersion} not configured for decryption`);
const decipher = createDecipheriv(
'aes-256-gcm',
key,
Buffer.from(payload.iv, 'hex')
);
decipher.setAuthTag(Buffer.from(payload.tag, 'hex'));
let plaintext = decipher.update(payload.ciphertext, 'hex', 'utf8');
plaintext += decipher.final('utf8');
return plaintext;
}
}
2. Enforcing TLS 1.3 in Transit
GDPR compliance requires eliminating deprecated cryptographic protocols (TLS 1.0, 1.1, and legacy cipher suites). All APIs and web applications must mandate TLS 1.3 or TLS 1.2 with Perfect Forward Secrecy (PFS).
NGINX Hardened TLS Configuration
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
# SSL Certificate Paths
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
# Enforce TLS 1.2 and 1.3 exclusively
ssl_protocols TLSv1.2 TLSv1.3;
# High-security AEAD cipher suites (PFS enabled)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS - Enforce HTTPS for 2 years including subdomains & preload
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
# SSL Session Caching
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
}
3. Verification Checklist & Compliance Matrix
| Requirement | Implementation Target | GDPR Article Mapping | Verification Command |
|---|---|---|---|
| Field Encryption | AES-256-GCM with 96-bit IV | Article 32(1)(a) | npm test cryptographic unit tests |
| TLS Protocol | Minimum TLS 1.2 / Preferred TLS 1.3 | Article 32(1)(a) | npx testssl.sh api.yourdomain.com |
| Key Rotation | Automated key version tag tracking | Article 32(1)(d) | Zero-downtime key rotation pipeline |
| Header Security | HSTS max-age=63072000 |
Article 25 | curl -I https://api.yourdomain.com |
Conclusion & Next Steps
Implementing cryptographic protection at both the network tier (TLS 1.3) and the database field tier (AES-256-GCM) ensures that even in the event of an infrastructure breach, customer PII remains mathematically unreadable without authorization key access.