Voltar ao blog

LGPD em Chatbots: Checklist de Seguranca e Conformidade

Guia tecnico para adequar seu chatbot a LGPD. Consentimento, retencao de dados, direitos do titular e implementacao pratica.

RS
Richard Sakaguchi Solution Architect
LGPD em Chatbots: Checklist de Seguranca e Conformidade

Chatbot que coleta dados pessoais precisa seguir a LGPD. Este guia cobre os requisitos tecnicos e como implementar cada um.

O Que a LGPD Exige de Chatbots

Dados Pessoais em Chatbots

TipoExemplosClassificacao
IdentificacaoNome, CPF, RGPessoal
ContatoTelefone, email, enderecoPessoal
FinanceiroCartao, conta bancariaPessoal Sensivel
SaudeSintomas, receitasPessoal Sensivel
LocalizacaoGPS, cidadePessoal
ComportamentalHistorico de comprasPessoal

Bases Legais Aplicaveis

Para chatbots, as bases mais comuns sao:

  1. Consentimento: Usuario autoriza explicitamente
  2. Execucao de contrato: Necessario para prestar servico
  3. Legitimo interesse: Beneficio mutuo claro

Checklist Tecnico de Conformidade

1. Coleta de Consentimento

Fluxo de Opt-in

const CONSENT_MESSAGE = `
Antes de continuar, preciso da sua autorizacao.

Vou coletar e processar seus dados (nome, telefone, historico de conversas) para:
- Atender suas solicitacoes
- Melhorar nosso atendimento
- Enviar atualizacoes sobre seus pedidos

Seus dados sao protegidos conforme nossa Politica de Privacidade: https://seusite.com/privacidade

Voce concorda? Responda SIM ou NAO.
`;

async function handleFirstContact(userId, message) {
  const consent = await getConsent(userId);

  if (!consent) {
    // Primeiro contato - pedir consentimento
    await sendMessage(userId, CONSENT_MESSAGE);
    await setAwaitingConsent(userId, true);
    return;
  }

  if (consent.awaitingResponse) {
    return handleConsentResponse(userId, message);
  }

  // Consentimento ja dado - processar normalmente
  return processMessage(userId, message);
}

async function handleConsentResponse(userId, response) {
  const normalized = response.trim().toUpperCase();

  if (normalized === 'SIM') {
    await saveConsent(userId, {
      granted: true,
      timestamp: new Date(),
      version: '1.0',
      ip: null,  // WhatsApp nao fornece
      method: 'whatsapp_chat'
    });

    await sendMessage(userId, 'Obrigado! Como posso ajudar?');

  } else if (normalized === 'NAO') {
    await saveConsent(userId, {
      granted: false,
      timestamp: new Date()
    });

    await sendMessage(userId,
      'Entendido. Sem sua autorizacao, nao podemos prosseguir com o atendimento. ' +
      'Se mudar de ideia, e so enviar uma mensagem.'
    );

  } else {
    await sendMessage(userId,
      'Por favor, responda SIM para autorizar ou NAO para recusar.'
    );
  }
}

Registro de Consentimento

// Schema MongoDB
const consentSchema = new mongoose.Schema({
  userId: { type: String, required: true, index: true },
  granted: { type: Boolean, required: true },
  timestamp: { type: Date, default: Date.now },
  version: String,  // Versao da politica aceita
  purposes: [String],  // Finalidades autorizadas
  method: String,  // Como foi coletado
  withdrawnAt: Date,  // Se revogou
  history: [{
    action: String,
    timestamp: Date,
    details: Object
  }]
});

// Funcoes
async function saveConsent(userId, data) {
  const consent = await Consent.findOne({ userId });

  if (consent) {
    consent.history.push({
      action: data.granted ? 'granted' : 'denied',
      timestamp: new Date(),
      details: data
    });
    consent.granted = data.granted;
    consent.timestamp = new Date();
    await consent.save();
  } else {
    await Consent.create({
      userId,
      ...data,
      history: [{
        action: 'initial',
        timestamp: new Date(),
        details: data
      }]
    });
  }
}

async function withdrawConsent(userId) {
  const consent = await Consent.findOne({ userId });
  if (consent) {
    consent.granted = false;
    consent.withdrawnAt = new Date();
    consent.history.push({
      action: 'withdrawn',
      timestamp: new Date()
    });
    await consent.save();
  }
}

2. Direitos do Titular

A LGPD garante direitos que seu chatbot deve suportar:

const PRIVACY_MENU = `
*Seus Direitos de Privacidade*

1 - Ver meus dados
2 - Corrigir meus dados
3 - Deletar meus dados
4 - Exportar meus dados
5 - Revogar consentimento
6 - Falar com humano

Escolha uma opcao:
`;

async function handlePrivacyRequest(userId, option) {
  switch (option) {
    case '1':
      return handleAccessRequest(userId);
    case '2':
      return handleCorrectionRequest(userId);
    case '3':
      return handleDeletionRequest(userId);
    case '4':
      return handleExportRequest(userId);
    case '5':
      return handleConsentWithdrawal(userId);
    case '6':
      return escalateToHuman(userId, 'privacy_request');
  }
}

Acesso aos Dados

async function handleAccessRequest(userId) {
  // Coletar todos os dados do usuario
  const userData = {
    profile: await getProfile(userId),
    conversations: await getConversations(userId, { limit: 100 }),
    orders: await getOrders(userId),
    consents: await getConsentHistory(userId)
  };

  // Formatar para exibicao
  const summary = `
*Seus Dados Armazenados*

Nome: ${userData.profile.name || 'Nao informado'}
Telefone: ${userId}
Email: ${userData.profile.email || 'Nao informado'}

Conversas armazenadas: ${userData.conversations.length}
Pedidos registrados: ${userData.orders.length}

Consentimento: ${userData.consents.granted ? 'Ativo' : 'Revogado'}
Data: ${userData.consents.timestamp}

Para relatorio completo em PDF, responda EXPORTAR.
`;

  await sendMessage(userId, summary);
}

Exclusao de Dados

async function handleDeletionRequest(userId) {
  // Confirmar antes de deletar
  await sendMessage(userId,
    'Voce tem certeza que deseja excluir todos os seus dados? ' +
    'Esta acao e irreversivel.\n\n' +
    'Responda CONFIRMAR para prosseguir.'
  );

  await setAwaitingDeletionConfirmation(userId, true);
}

async function confirmDeletion(userId) {
  // Anonimizar dados (nao deletar completamente para auditoria)
  await anonymizeUser(userId);

  // Log da exclusao (obrigatorio manter registro da acao)
  await logDeletionRequest({
    originalUserId: hashUserId(userId),  // Hash para nao identificar
    requestedAt: new Date(),
    executedAt: new Date(),
    dataTypes: ['profile', 'conversations', 'orders']
  });

  await sendMessage(userId,
    'Seus dados foram excluidos. ' +
    'Mantemos apenas um registro anonimo desta solicitacao por obrigacao legal.'
  );
}

async function anonymizeUser(userId) {
  // Anonimizar perfil
  await db.profiles.updateOne(
    { userId },
    {
      $set: {
        name: '[REMOVIDO]',
        email: '[REMOVIDO]',
        phone: hashUserId(userId),
        anonymizedAt: new Date()
      }
    }
  );

  // Anonimizar conversas
  await db.conversations.updateMany(
    { userId },
    {
      $set: {
        'messages.$[].content': '[REMOVIDO]',
        anonymizedAt: new Date()
      }
    }
  );

  // Revogar consentimento
  await withdrawConsent(userId);
}

Exportacao (Portabilidade)

async function handleExportRequest(userId) {
  // Gerar arquivo com todos os dados
  const userData = await collectAllUserData(userId);

  const exportData = {
    exportDate: new Date().toISOString(),
    userId: userId,
    data: userData
  };

  // Salvar como JSON
  const fileName = `dados_${hashUserId(userId)}_${Date.now()}.json`;
  const filePath = `/tmp/exports/${fileName}`;

  fs.writeFileSync(filePath, JSON.stringify(exportData, null, 2));

  // Enviar link seguro (expira em 24h)
  const downloadUrl = await createSecureDownloadLink(filePath, 24 * 60 * 60);

  await sendMessage(userId,
    `Seus dados estao prontos para download:\n${downloadUrl}\n\n` +
    'Link valido por 24 horas.'
  );

  // Agendar exclusao do arquivo
  setTimeout(() => fs.unlinkSync(filePath), 24 * 60 * 60 * 1000);
}

3. Retencao de Dados

Politica de Retencao

const RETENTION_POLICY = {
  conversations: {
    active: 90,      // Dias para manter conversas ativas
    archived: 365,   // Dias para manter arquivadas
    afterConsent: 30 // Dias apos revogacao
  },
  orders: {
    completed: 5 * 365,  // 5 anos (obrigacao fiscal)
    cancelled: 365
  },
  logs: {
    access: 180,
    security: 365
  }
};

// Job de limpeza (rodar diariamente)
async function cleanupExpiredData() {
  const now = new Date();

  // Limpar conversas antigas
  const conversationCutoff = new Date(
    now - RETENTION_POLICY.conversations.archived * 24 * 60 * 60 * 1000
  );

  await db.conversations.deleteMany({
    updatedAt: { $lt: conversationCutoff },
    status: 'archived'
  });

  // Anonimizar usuarios com consentimento revogado ha mais de 30 dias
  const consentCutoff = new Date(
    now - RETENTION_POLICY.conversations.afterConsent * 24 * 60 * 60 * 1000
  );

  const expiredConsents = await Consent.find({
    granted: false,
    withdrawnAt: { $lt: consentCutoff }
  });

  for (const consent of expiredConsents) {
    await anonymizeUser(consent.userId);
  }

  console.log(`Limpeza concluida: ${expiredConsents.length} usuarios anonimizados`);
}

// Agendar job
const cron = require('node-cron');
cron.schedule('0 3 * * *', cleanupExpiredData);  // 3h da manha

4. Seguranca dos Dados

Criptografia em Transito

const https = require('https');
const fs = require('fs');

// Servidor HTTPS obrigatorio
const server = https.createServer({
  key: fs.readFileSync('/path/to/private.key'),
  cert: fs.readFileSync('/path/to/certificate.crt'),
  minVersion: 'TLSv1.2'  // Minimo TLS 1.2
}, app);

Criptografia em Repouso

const crypto = require('crypto');

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY;  // 32 bytes
const IV_LENGTH = 16;

function encrypt(text) {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv('aes-256-gcm', ENCRYPTION_KEY, iv);

  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');

  const authTag = cipher.getAuthTag().toString('hex');

  return `${iv.toString('hex')}:${authTag}:${encrypted}`;
}

function decrypt(encryptedText) {
  const [ivHex, authTagHex, encrypted] = encryptedText.split(':');

  const iv = Buffer.from(ivHex, 'hex');
  const authTag = Buffer.from(authTagHex, 'hex');
  const decipher = crypto.createDecipheriv('aes-256-gcm', ENCRYPTION_KEY, iv);

  decipher.setAuthTag(authTag);

  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');

  return decrypted;
}

// Uso no schema
const messageSchema = new mongoose.Schema({
  userId: String,
  content: {
    type: String,
    set: encrypt,
    get: decrypt
  }
});

Mascaramento de Dados Sensiveis

function maskSensitiveData(data) {
  return {
    ...data,
    cpf: data.cpf ? maskCPF(data.cpf) : null,
    phone: data.phone ? maskPhone(data.phone) : null,
    email: data.email ? maskEmail(data.email) : null,
    creditCard: data.creditCard ? maskCard(data.creditCard) : null
  };
}

function maskCPF(cpf) {
  // 123.456.789-00 -> ***.***.789-**
  return cpf.replace(/(\d{3})\.(\d{3})\.(\d{3})-(\d{2})/, '***.***.***-**');
}

function maskPhone(phone) {
  // 11999999999 -> 11*****9999
  return phone.slice(0, 2) + '*****' + phone.slice(-4);
}

function maskEmail(email) {
  // usuario@email.com -> u***o@email.com
  const [user, domain] = email.split('@');
  return user[0] + '***' + user.slice(-1) + '@' + domain;
}

// Usar em logs
console.log('Usuario:', maskSensitiveData(userData));

5. Logs de Auditoria

const auditSchema = new mongoose.Schema({
  timestamp: { type: Date, default: Date.now, index: true },
  action: {
    type: String,
    enum: ['access', 'create', 'update', 'delete', 'export', 'consent'],
    index: true
  },
  userId: { type: String, index: true },  // Usuario afetado
  performedBy: String,  // Quem executou (bot, admin, etc)
  resource: String,  // Tipo de dado
  details: Object,
  ip: String,
  userAgent: String
});

async function logAudit(action, userId, details) {
  await Audit.create({
    action,
    userId,
    performedBy: 'chatbot',
    resource: details.resource,
    details: {
      ...details,
      // Nao logar dados sensiveis
      sensitiveFields: details.fields?.filter(f =>
        ['cpf', 'password', 'card'].includes(f)
      ).length || 0
    }
  });
}

// Uso
await logAudit('access', userId, {
  resource: 'profile',
  fields: ['name', 'email', 'phone']
});

await logAudit('export', userId, {
  resource: 'all_data',
  format: 'json',
  requestedAt: new Date()
});

6. Incidentes de Seguranca

async function handleSecurityIncident(incident) {
  // 1. Registrar incidente
  const incidentRecord = await Incident.create({
    type: incident.type,
    severity: incident.severity,
    affectedUsers: incident.users,
    description: incident.description,
    detectedAt: new Date(),
    status: 'open'
  });

  // 2. Notificar equipe
  await notifySecurityTeam(incidentRecord);

  // 3. Se grave, notificar ANPD (72h)
  if (incident.severity === 'high' && incident.affectsPersonalData) {
    await scheduleANPDNotification(incidentRecord);
  }

  // 4. Se necessario, notificar usuarios afetados
  if (incident.requiresUserNotification) {
    for (const userId of incident.users) {
      await notifyUser(userId, {
        title: 'Aviso de Seguranca',
        message: incident.userMessage
      });
    }
  }

  return incidentRecord;
}

Implementacao de DPO Virtual

// Comandos de privacidade reconhecidos
const PRIVACY_COMMANDS = [
  'meus dados', 'minha privacidade', 'lgpd',
  'deletar dados', 'excluir dados', 'apagar dados',
  'exportar dados', 'baixar dados',
  'revogar consentimento', 'cancelar autorizacao',
  'falar com dpo', 'encarregado'
];

function isPrivacyRequest(message) {
  const lower = message.toLowerCase();
  return PRIVACY_COMMANDS.some(cmd => lower.includes(cmd));
}

async function handleMessage(userId, message) {
  // Verificar se e solicitacao de privacidade
  if (isPrivacyRequest(message)) {
    return handlePrivacyRequest(userId, message);
  }

  // Processamento normal
  return processNormalMessage(userId, message);
}

async function handlePrivacyRequest(userId, message) {
  const lower = message.toLowerCase();

  if (lower.includes('deletar') || lower.includes('excluir') || lower.includes('apagar')) {
    return handleDeletionRequest(userId);
  }

  if (lower.includes('exportar') || lower.includes('baixar')) {
    return handleExportRequest(userId);
  }

  if (lower.includes('revogar') || lower.includes('cancelar')) {
    return handleConsentWithdrawal(userId);
  }

  if (lower.includes('dpo') || lower.includes('encarregado')) {
    return provideDPOContact(userId);
  }

  // Menu geral de privacidade
  return sendMessage(userId, PRIVACY_MENU);
}

function provideDPOContact(userId) {
  return sendMessage(userId,
    '*Encarregado de Dados (DPO)*\n\n' +
    'Nome: [Nome do DPO]\n' +
    'Email: dpo@suaempresa.com.br\n' +
    'Telefone: (11) 1234-5678\n\n' +
    'Horario: Segunda a Sexta, 9h-18h'
  );
}

Checklist Final

Coleta

  • Consentimento explicito antes de coletar dados
  • Finalidade clara e especifica
  • Coleta minima (so o necessario)
  • Registro de consentimento com timestamp

Armazenamento

  • Criptografia em transito (HTTPS/TLS 1.2+)
  • Criptografia em repouso para dados sensiveis
  • Backup com mesma protecao
  • Politica de retencao implementada

Direitos do Titular

  • Acesso aos dados (visualizacao)
  • Correcao de dados
  • Exclusao/anonimizacao
  • Portabilidade (export)
  • Revogacao de consentimento

Seguranca

  • Logs de auditoria
  • Controle de acesso
  • Mascaramento em logs
  • Plano de resposta a incidentes
  • Contato do DPO disponivel

Documentacao

  • Politica de privacidade acessivel
  • Termos de uso claros
  • Registro de operacoes de tratamento
  • Relatorio de impacto (se aplicavel)

Penalidades

InfracaoMulta
Advertencia-
Multa simplesAte 2% do faturamento (max R$ 50M)
Multa diariaAte 2% do faturamento/dia
Bloqueio de dados-
Eliminacao de dados-

Prevenir e mais barato que remediar.


Sakaguchi IA - Inteligencia Artificial para Empresas Brasileiras

Gostou do conteudo?

Descubra como implementar IA no seu negocio com uma analise gratuita.

Agendar Analise Gratuita

Pronto para automatizar seu atendimento?

Agende uma analise gratuita e descubra como IA pode transformar seu negocio.

Agendar Analise Gratuita