Criptografia é a única medida do Art. 46 LGPD (segurança técnica) que ANPD inspeciona objetivamente — existe ou não, com força x ou y. Este módulo é o playbook prático: TLS 1.3 nas pontas, mTLS interno quando faz sentido, AES-256-GCM em storage com envelope encryption, KMS gerenciando KEK, rotação configurada, e a fronteira BYOK/HYOK quando regulação exige. Sem mistificação.
Camadas — onde a criptografia mora
TLS 1.3 — o mínimo aceitável
# TLS 1.3 only (em 2026 já dá pra exigir)
ssl_protocols TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_prefer_server_ciphers off; # TLS 1.3 client preference
ssl_session_tickets off; # forward secrecy
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Mozilla Modern: https://ssl-config.mozilla.org/Envelope encryption — o padrão de storage
import { KMSClient, GenerateDataKeyCommand, DecryptCommand } from '@aws-sdk/client-kms';
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
const kms = new KMSClient({ region: 'sa-east-1' });
const KEK_ARN = process.env.KMS_KEK_ARN!;
export interface EncryptedPayload {
ciphertext: Buffer;
ciphertextDek: Buffer;
nonce: Buffer;
tag: Buffer;
aad?: Buffer;
}
export async function encrypt(plaintext: Buffer, aad?: Buffer): Promise<EncryptedPayload> {
const { Plaintext, CiphertextBlob } = await kms.send(new GenerateDataKeyCommand({
KeyId: KEK_ARN, KeySpec: 'AES_256',
}));
const dek = Buffer.from(Plaintext!);
const nonce = randomBytes(12);
const cipher = createCipheriv('aes-256-gcm', dek, nonce);
if (aad) cipher.setAAD(aad);
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const tag = cipher.getAuthTag();
// CRITICAL: zerar DEK em memória
dek.fill(0);
return { ciphertext, ciphertextDek: Buffer.from(CiphertextBlob!), nonce, tag, aad };
}
export async function decrypt(p: EncryptedPayload): Promise<Buffer> {
const { Plaintext } = await kms.send(new DecryptCommand({ CiphertextBlob: p.ciphertextDek }));
const dek = Buffer.from(Plaintext!);
const decipher = createDecipheriv('aes-256-gcm', dek, p.nonce);
if (p.aad) decipher.setAAD(p.aad);
decipher.setAuthTag(p.tag);
const result = Buffer.concat([decipher.update(p.ciphertext), decipher.final()]);
dek.fill(0);
return result;
}AAD (Additional Authenticated Data) não é criptografado, mas é autenticado. Use para amarrar ciphertext ao contexto: . Impede swap attack (mover ciphertext entre registros).
AES-GCM — anatomia e armadilhas
Nonce reuse com mesma chave em GCM é catastrófico: vaza XOR de keystreams e permite forjar mensagens autenticadas. NUNCA gere nonce com PRNG fraco. Use (CSPRNG) ou contador atômico monotônico por chave. Em alta volume, considere AES-256-GCM-SIV (RFC 8452) — resistente a nonce reuse.
Comparativo KMS — AWS, GCP, Azure, Vault
| Recurso | AWS KMS | GCP Cloud KMS | Azure Key Vault | HashiCorp Vault Transit |
|---|---|---|---|---|
| HSM nível | FIPS 140-2 L3 (CloudHSM L3) | FIPS 140-2 L3 opcional | Managed HSM FIPS L3 | FIPS 140-2 L3 com seal HSM |
| Auto-rotation | Sim, 1 ano | Configurável | Sim, configurável | Sim, configurável |
| BYOK | External Key Store (XKS) | External Key Manager (EKM) | BYOK suportado | Não aplicável (você opera) |
| Audit | CloudTrail | Cloud Audit Logs | Activity Log | Audit device |
| Custo (USD) | $1/key/mês + $0.03/10k | $0.06/key/mês + $0.03/10k | ~$1/key + ops | Self-host |
| Per-record encrypt | GenerateDataKey + DEK | GenerateRandomBytes + AEAD local | wrap/unwrap | Encrypt API |
| Multi-region | KMS multi-region keys | Replicated keysets | Geo-replicated | Replication enterprise |
mTLS interno — service mesh ou manual?
📋 Cluster Kubernetes com 30 serviços, precisa autenticação service-to-service auditável
Sidecar injeta TLS sem mudança de código, identidade via SPIFFE (spiffe://cluster/ns/sa), rotação automática de certs (1h–24h via cert-manager + Vault PKI), audit log com identidade real do caller. Manual exige boilerplate em cada serviço e divergência inevitável.
Alt: Manual TLS no app —
Alt: JWT entre serviços —
Alt: Apenas IAM/VPC —
Quando criptografia at-rest não basta
EBS-encrypted, RDS-encrypted, S3 SSE — todos protegem contra roubo de disco. Não protegem contra credencial vazada nem contra insider com acesso de leitura legítimo. Para PII sensível (CPF + biometria, saúde, dados financeiros), aplique application-layer encryption com chave por tenant ou por campo. Operações analíticas ficam mais complexas — use Searchable Encryption (CipherStash) ou Tokenization (Vault, AWS Payment Cryptography) se precisar buscar/joinar.
Erros frequentes em produção
Checklist de auditoria pre-incidente
- TLS 1.3 (ou 1.2 com AEAD only) em todo endpoint público? Rode .
- HSTS preload, OCSP stapling, cert auto-renew (ACM/Let’s Encrypt)?
- mTLS entre serviços internos críticos? Service mesh com SPIFFE?
- RDS/Aurora + ?
- S3 buckets com SSE-KMS + Bucket Key + Block Public Access?
- EBS volumes encrypted by default no account?
- Envelope encryption em app-layer para PII sensível?
- KMS key rotation habilitada? CloudTrail data events ligados?
- Backups com encryption + chave separada da prod?
- Plano de rotação de emergência documentado (24h)?
- Algorithms aprovados (NIST SP 800-131A Rev 2)? Sem MD5/SHA-1/RC4/3DES?
- Tokenização para CPF/cartão onde plaintext na app é desnecessário?