Criptografia
Criptografia e descriptografia de campos sensíveis em repouso (AES-256-GCM).
Campos sensíveis (PII) são cifrados em repouso com AES-256-GCM, transparente para os services, via uma Prisma Client Extension. Toda a lógica está em lib/services/crypto/.
Motor (encryption-engine.ts)
encrypt(texto): cifra e retorna"v1:<base64(iv || authTag || ciphertext)>". Cada valor recebe um IV aleatório de 12 bytes — o mesmo texto claro nunca produz o mesmo cifrado duas vezes.decrypt(valorCifrado): decifra, lançando se oauthTagnão bater (indica adulteração).hashForLookup(texto): HMAC-SHA256 determinístico, usado para permitir busca exata (WHERE) sobre um campo cifrado.
A chave vem de ENCRYPTION_KEY (32 bytes em base64, gerar com openssl rand -base64 32).
Extensão (encryption-extension.ts)
A extensão intercepta toda query do Prisma e:
- Em
create/update/upsertnos models listados emMODELS_COM_PII_CIFRADA(hoje sóUser), cifra os campos configurados emCAMPOS_COM_HASHe preenche o<campo>Hashcorrespondente. - Reescreve
where: { email: "..." }parawhere: { emailHash: hash(...) }, já que o valor cifrado não bate por igualdade direta. - Decifra recursivamente qualquer campo cifrado no resultado, inclusive em relations aninhadas via
include/select.
Adicionando um novo campo cifrado
Em encryption-extension.ts:
- Adicione o model a
MODELS_COM_PII_CIFRADA, se ainda não estiver. - Se o campo precisa de busca exata (
WHERE), adicione emCAMPOS_COM_HASHcom o nome do campo<campo>Hashcorrespondente no schema Prisma (colunaString @unique, ou indexada conforme o caso). - Ajuste
SemCamposHash(lib/services/crypto/tipos.ts) para continuar omitindo o novo<campo>Hashdos tipos de input do Prisma usados pelos services — quem preenche esse campo é sempre a extensão, nunca o chamador.
Uso
Os services passam o valor em texto claro normalmente (db.user.create({ data: { email: "..." } })) — a extensão cuida de cifrar antes de persistir e decifrar ao ler. Nenhuma chamada manual a encrypt/decrypt é necessária no código de negócio.