Compliance para devs: LGPD e GDPR traduzidos em decisões de código
1. Fundamentos Legais e Mapeamento de Dados no Código
A implementação de compliance começa com a tradução de bases legais em variáveis de estado. Cada operação de tratamento deve estar associada a uma base legal específica, controlada por enums e verificada em tempo de execução.
enum BaseLegal {
CONSENTIMENTO = "consentimento",
LEGITIMO_INTERESSE = "legitimo_interesse",
EXECUCAO_CONTRATUAL = "execucao_contratual",
OBRIGACAO_LEGAL = "obrigacao_legal"
}
class DadosPessoais {
constructor(dado, baseLegal, finalidade) {
this.dado = dado;
this.baseLegal = baseLegal;
this.finalidade = finalidade;
this.dataColeta = new Date().toISOString();
}
}
O mapeamento de fluxos exige um inventário automatizado. Use anotações em decorators para marcar campos sensíveis:
function DadoPessoal(categoria, finalidade) {
return function(target, propertyKey) {
if (!target.__inventario) target.__inventario = [];
target.__inventario.push({
campo: propertyKey,
categoria: categoria,
finalidade: finalidade
});
};
}
class Usuario {
@DadoPessoal("identificacao", "cadastro")
cpf;
@DadoPessoal("contato", "comunicacao")
email;
}
A diferenciação entre controlador (quem decide) e operador (quem executa) deve refletir em camadas de arquitetura: serviços internos como controladores, APIs de terceiros como operadores.
2. Consentimento e Gerenciamento de Preferências do Usuário
O consentimento granular exige persistência com metadados de versão:
CREATE TABLE consentimentos (
id UUID PRIMARY KEY,
usuario_id UUID NOT NULL,
finalidade VARCHAR(100) NOT NULL,
concedido BOOLEAN NOT NULL,
versao_termo VARCHAR(20) NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_origem VARCHAR(45),
UNIQUE(usuario_id, finalidade)
);
A revogação deve disparar uma rotina de tratamento:
function revogarConsentimento(usuarioId, finalidade) {
db.exec("UPDATE consentimentos SET concedido = false WHERE usuario_id = ? AND finalidade = ?", [usuarioId, finalidade]);
// Dispara job para anonimizar dados daquela finalidade
jobQueue.add("anonimizar_dados", {
usuarioId: usuarioId,
finalidade: finalidade
});
}
Logs de consentimento com timestamp e versão do termo garantem auditabilidade:
function registrarConsentimento(usuarioId, finalidade, concedido, versao) {
const log = {
usuarioId,
finalidade,
concedido,
versao,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
};
db.collection("logs_consentimento").insertOne(log);
}
3. Minimização de Dados e Retenção Programada
Schemas de banco devem refletir o princípio da minimização:
CREATE TABLE pedidos (
id UUID PRIMARY KEY,
valor_total DECIMAL(10,2),
data_pedido DATE,
-- NUNCA armazenar dados de cartão de crédito aqui
-- Apenas referência ao token do gateway
token_pagamento VARCHAR(100)
);
Políticas de retenção com jobs agendados:
// Job cron: executa toda madrugada
function jobExpiracaoDados() {
const dataLimite = new Date();
dataLimite.setMonth(dataLimite.getMonth() - 6); // 6 meses de retenção
db.exec(
"DELETE FROM logs_navegacao WHERE data_hora < ?",
[dataLimite.toISOString()]
);
db.exec(
"UPDATE usuarios SET dados_navegacao = NULL WHERE ultimo_acesso < ?",
[dataLimite.toISOString()]
);
}
Anonimização com hash e salting:
function pseudonimizarCPF(cpf) {
const salt = crypto.randomBytes(16).toString("hex");
const hash = crypto.createHash("sha256").update(cpf + salt).digest("hex");
return { hash, salt };
}
4. Direitos dos Titulares: Acesso, Retificação e Exclusão
Endpoint de exportação de dados:
app.get("/api/v1/dados/exportar", async (req, res) => {
const usuario = await db.collection("usuarios").findOne({ _id: req.userId });
const pedidos = await db.collection("pedidos").find({ usuario_id: req.userId }).toArray();
const dossie = {
dadosCadastrais: usuario,
historicoPedidos: pedidos,
logsConsentimento: await db.collection("logs_consentimento").find({ usuarioId: req.userId }).toArray(),
dataExportacao: new Date().toISOString()
};
res.setHeader("Content-Type", "application/json");
res.setHeader("Content-Disposition", "attachment; filename=dados_pessoais.json");
res.json(dossie);
});
Exclusão lógica com rastreabilidade:
function softDeleteUsuario(usuarioId) {
db.collection("usuarios").updateOne(
{ _id: usuarioId },
{ $set: {
deleted_at: new Date().toISOString(),
deleted_by: "solicitacao_titular",
motivo: "direito_ao_esquecimento"
}}
);
// Job assíncrono para exclusão física após 30 dias
jobQueue.add("exclusao_fisica", { usuarioId, agendarPara: "30d" });
}
5. Segurança de Dados e Notificação de Violações
Criptografia em repouso com AES-256:
const algoritmo = "aes-256-gcm";
const chave = process.env.CHAVE_CRIPTOGRAFIA;
function criptografar(texto) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algoritmo, Buffer.from(chave, "hex"), iv);
let encrypted = cipher.update(texto, "utf8", "hex");
encrypted += cipher.final("hex");
return iv.toString("hex") + ":" + encrypted;
}
Detecção de vazamento com rate limiting e logs anômalos:
function detectarVazamento(logEntry) {
const anomalias = [];
if (logEntry.camposAcessados.length > 50) {
anomalias.push("ACESSO_MACICO_DADOS");
}
if (logEntry.horario < "06:00" || logEntry.horario > "22:00") {
anomalias.push("HORARIO_ATIPICO");
}
if (anomalias.length > 0) {
notificarDPO(anomalias, logEntry);
}
}
Trigger de notificação para ANPD:
function notificarViolacao(detalhes) {
const template = {
dataOcorrencia: new Date().toISOString(),
natureza: detalhes.tipo,
categoriasDados: detalhes.dadosAfetados,
numeroTitulares: detalhes.quantidadeUsuarios,
medidasMitigadoras: detalhes.acoesTomadas,
contatoDPO: "dpo@empresa.com.br"
};
emailService.enviar("anpd@anpd.gov.br", "Notificação de Violação", template);
emailService.enviar("dpo@empresa.com.br", "Alerta Interno", detalhes);
}
6. Transferência Internacional e Armazenamento Regional
Lógica condicional para escolha de cluster de banco:
function obterCluster(regiaoUsuario) {
const clusters = {
"BR": "sa-east-1",
"EU": "eu-west-1",
"US": "us-east-1"
};
return clusters[regiaoUsuario] || "sa-east-1";
}
async function salvarDados(usuario, dados) {
const cluster = obterCluster(usuario.regiao);
const db = await conectarCluster(cluster);
await db.collection("dados_pessoais").insertOne(dados);
}
Validação automática de SCCs em integrações:
function validarIntegracao(parceiro) {
if (parceiro.pais !== "BR" && !parceiro.sccAssinado) {
throw new Error("Transferência internacional sem SCCs válidas");
}
if (parceiro.pais === "US" && !parceiro.privacyShield) {
throw new Error("Empresa dos EUA sem certificação Privacy Shield");
}
}
7. Auditoria, Logs e Documentação Automatizada
Logs imutáveis de acesso a dados pessoais:
CREATE TABLE logs_acesso (
id SERIAL PRIMARY KEY,
usuario_id UUID NOT NULL,
campo_acessado VARCHAR(100) NOT NULL,
finalidade VARCHAR(200) NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_origem VARCHAR(45),
user_agent TEXT,
-- Trigger impede UPDATE/DELETE
CHECK (1=1) -- Imutabilidade garantida por trigger no banco
);
-- Trigger de imutabilidade
CREATE OR REPLACE FUNCTION impedir_alteracao_log()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'Logs de acesso são imutáveis';
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_imutavel
BEFORE UPDATE OR DELETE ON logs_acesso
FOR EACH ROW EXECUTE FUNCTION impedir_alteracao_log();
Geração automática de RAT via CI/CD:
# script/gerar_rat.js
const inventario = require("./inventario_dados.json");
function gerarRAT() {
const rat = {
dataGeracao: new Date().toISOString(),
versao: process.env.CI_COMMIT_SHA,
controlador: "Empresa X Ltda",
encarregado: "dpo@empresa.com.br",
operacoes: inventario.map(op => ({
finalidade: op.finalidade,
baseLegal: op.baseLegal,
categoriasDados: op.campos,
prazoRetencao: op.retencao,
medidasSeguranca: ["criptografia_aes256", "tls13", "logs_imutaveis"]
}))
};
fs.writeFileSync("rat_automatico.json", JSON.stringify(rat, null, 2));
}
Testes unitários de compliance:
describe("Regras de Privacidade", () => {
it("CPF não deve aparecer em logs de texto claro", () => {
const log = gerarLogAcesso({ cpf: "123.456.789-00" });
expect(log.mensagem).not.toContain("123.456.789-00");
expect(log.mensagem).toContain("***.***.***-**");
});
it("consentimento deve ser registrado antes de qualquer tratamento", () => {
const usuario = new Usuario({ email: "teste@teste.com" });
expect(() => usuario.processarDados()).toThrow("Consentimento não registrado");
});
});
Referências
- LGPD - Lei Geral de Proteção de Dados (Lei 13.709/2018) — Texto oficial da lei brasileira de proteção de dados pessoais
- GDPR - General Data Protection Regulation (EU) 2016/679 — Regulamento europeu de proteção de dados, com guias práticos e checklist de compliance
- OWASP Top 10 Privacy Risks — Riscos de privacidade no desenvolvimento de software, com exemplos de código vulnerável
- Documentação oficial Node.js Crypto — Módulo de criptografia do Node.js para implementação de hash, AES e geração de chaves
- ANPD - Autoridade Nacional de Proteção de Dados — Guias, sanções e comunicados oficiais sobre a aplicação da LGPD no Brasil
- MongoDB Security Checklist — Práticas de segurança para banco de dados, incluindo criptografia em repouso e controle de acesso
- PostgreSQL Triggers Documentation — Documentação oficial sobre triggers no PostgreSQL para implementação de logs imutáveis