13 KiB
06 — LGPD e segurança
Este documento tem consequência jurídica real. O condomínio é controlador dos dados; a plataforma é operadora. Um erro de base legal aqui não é bug — é multa e responsabilidade civil. Não substitui parecer jurídico. Antes de operar comercialmente, valide com advogado especializado.
1. A base legal — e por que a intuição está errada
A intuição diz: "peça consentimento ao visitante". Está errada.
Consentimento na LGPD precisa ser livre (art. 5º, XII). Um visitante que só entra no prédio se aceitar ser fotografado não está consentindo livremente — está sob condição. Consentimento obtido assim é frágil e pode ser considerado inválido, derrubando toda a base de tratamento.
Além disso, o titular pode revogar o consentimento a qualquer momento (art. 8º, §5º). Um sistema de segurança cuja base legal evapora quando o visitante pede não serve como sistema de segurança.
Base legal correta: legítimo interesse — art. 7º, IX, para segurança patrimonial e das pessoas do condomínio. Em situações de risco à integridade física, o art. 7º, VII também sustenta.
| Dado | Base legal | Finalidade |
|---|---|---|
| Nome do visitante | Legítimo interesse (7º, IX) | Identificação de quem acessa |
| Foto do rosto | Legítimo interesse (7º, IX) | Validação visual pelo morador |
| Geolocalização | Legítimo interesse (7º, IX) | Anti-fraude: confirmar presença física na portaria |
| Documento (opcional) | Legítimo interesse (7º, IX) | Identificação em ocorrência |
| Gravação de chamada | Legítimo interesse + aviso reforçado | Prova em disputa (módulo opcional) |
| Dados do morador | Execução de contrato (7º, V) | Prestação do serviço |
A LIA é obrigatória
Legítimo interesse exige Legitimate Interest Assessment documentada e arquivada, com três etapas:
- Finalidade legítima — controle de acesso e segurança patrimonial são interesse legítimo pacífico.
- Necessidade — o dado é o mínimo? Foto do rosto: sim, é como o morador valida quem está lá fora. Geolocalização: sim, é o que impede acionamento remoto fraudulento. Documento: não é necessário — por isso é opcional e não bloqueia o fluxo.
- Balanceamento — o direito do titular prevalece? Aqui entram as salvaguardas: retenção curta, acesso restrito e auditado, sem uso secundário, sem compartilhamento, sem biometria.
Modelo de LIA em docs/anexos/LIA-modelo.md, a ser preenchido por condomínio (cada um é controlador dos seus dados).
O que muda na prática
Não existe tela de "Aceito os termos" com checkbox. Existe aviso de tratamento claro, exibido antes da coleta:
Este acesso é registrado. Sua imagem e localização são coletadas para segurança do condomínio, ficam guardadas por 90 dias e são acessíveis apenas ao morador que você procura e à administração. Base legal: legítimo interesse (LGPD, art. 7º, IX). [Saiba mais]
O que se registra em privacy_notices é a exibição do aviso — versão do texto, timestamp, IP, user agent. O que precisa ser provável é que a informação foi dada.
E uma placa física na portaria com o mesmo aviso, para quem entra sem usar o sistema.
2. Por que não há reconhecimento facial
Dado biométrico é dado pessoal sensível (art. 5º, II). Tratamento de dado sensível tem lista fechada de hipóteses (art. 11) e não admite legítimo interesse. Restaria consentimento — que, como visto, não é livre nesse contexto.
Ou seja: reconhecimento facial em portaria de visitante fica sem base legal sólida. É por isso que está fora do escopo — decisão jurídica, não limitação técnica.
Foto ≠ biometria. Uma foto guardada e vista por um humano é dado pessoal comum. Ela vira biométrica quando é processada para extrair template facial e identificar automaticamente. A fronteira é o processamento, não a imagem.
Regra de engenharia: nenhuma biblioteca de detecção ou extração facial entra no projeto, nem "só para enquadrar o rosto". A tentação aparece disfarçada de UX.
3. Direitos do titular
| Direito | Implementação | Prazo |
|---|---|---|
| Confirmação e acesso | POST /admin/lgpd/export por CPF ou telefone |
15 dias |
| Correção | Admin edita e registra em audit_log |
15 dias |
| Eliminação | POST /admin/lgpd/erase — apaga mídia, anonimiza nome |
15 dias |
| Portabilidade | Exportação em JSON | 15 dias |
| Informação sobre compartilhamento | Documentada no aviso | imediato |
| Oposição | Registrada; avaliada contra o legítimo interesse | 15 dias |
Eliminação não apaga a linha da visita. Apaga o dado pessoal (mídia, nome, documento) e mantém o registro anonimizado com data, unidade e resultado. Justificativa: obrigação de guarda de registro de acesso para segurança patrimonial — a linha vira estatística, não identificação.
O condomínio é quem responde ao titular; a plataforma fornece a ferramenta. Isso precisa estar no contrato de operador.
4. Retenção
| Dado | Prazo | Fundamento |
|---|---|---|
| Foto do visitante | 90 dias | Janela típica para descoberta de ocorrência |
| Foto de pacote | 30 dias | Extravio se descobre rápido |
| Recado em vídeo | 30 dias, ou até resolvido | Finalidade se esgota |
| Gravação de chamada | 90 dias | Só com módulo ativo e aviso reforçado |
| Registro da visita (sem mídia) | 5 anos | Segurança patrimonial |
audit_log |
5 anos | Prova de conformidade |
Job MEDIA_PURGE diário: apaga o objeto no MinIO, anula storage_key, preserva a linha em media_assets. Fica provado que existiu uma foto e que ela foi eliminada no prazo — que é exatamente o que se precisa demonstrar numa fiscalização.
Prazos são configuráveis por tenant, com teto definido pela plataforma. Um condomínio querendo guardar foto por 5 anos configuraria um risco que a plataforma não aceita hospedar.
5. Modelo de ameaças
A.1 — Enumeração de moradores (crítica)
Ataque: com o QR (visível na entrada, fotografável da calçada), alguém testa combinações de bloco e unidade e descobre quais existem e quem mora onde. É reconhecimento para assalto ou stalking — segurança física antes de privacidade.
Mitigação — busca cega. A API nunca revela se uma unidade existe:
- Visita criada mesmo com
unit_idnulo, preservandounit_input - Resposta idêntica em corpo, código e headers
- Tempo de resposta constante (delay artificial iguala os caminhos — sem isso o ataque vira timing attack)
- Linha do tempo de estados idêntica — a visita-sombra percorre os mesmos estados nos mesmos tempos, sem notificar ninguém (
03-FLUXOS-E-CONTRATOS.md§2). Sem isso, o atacante distingue os casos observando a sequência no WebSocket, e o tempo constante da resposta HTTP não protege nada - Nome do morador nunca aparece antes do atendimento
- Rate limit por IP e por dispositivo, com bloqueio progressivo
Regra de implementação: a tela do visitante nunca exibe autocomplete, sugestão ou validação de unidade. O campo é livre.
A.2 — Acionamento remoto fraudulento
Ataque: foto do QR, acionamento de casa, engenharia social por vídeo ("sou da manutenção").
Mitigação: geofence ST_DWithin contra gates.location; fora do raio, 403 OUTSIDE_GEOFENCE. Localização negada bloqueia o fluxo. O selo de distância aparece na tela do morador durante a chamada — ⚠ A 340m da portaria é o sinal mais claro possível.
Limitação assumida: GPS é falsificável com app rooteado. A defesa é dissuasória, não absoluta; complementada por rate limit e auditoria. Um atacante determinado passa — mas a tentativa fica registrada com foto.
A.3 — DoS social
Ataque: tocar em todos os apartamentos de madrugada.
Mitigação: condominiums.quiet_hours — na janela de silêncio, visitas não pré-autorizadas vão direto para operador ou recado, sem acordar moradores. Rate limit por dispositivo, bloqueio progressivo, e alerta no admin ao detectar padrão de varredura.
A.4 — Vazamento de mídia
Mitigação: bucket privado, nenhuma URL pública; acesso só por URL assinada de 5 minutos gerada sob autenticação; todo acesso registrado em audit_log; criptografia em repouso; sha256 de cada objeto como prova de integridade.
A.5 — Adulteração de auditoria
Ataque: síndico envolvido em ocorrência altera ou apaga registros.
Mitigação: audit_log é append-only, com UPDATE e DELETE revogados no banco para o usuário da aplicação:
REVOKE UPDATE, DELETE ON audit_log FROM portaria_app;
Nem a aplicação comprometida altera o log. Somado ao sha256 da mídia, isso é o que dá valor probatório ao registro.
Só funciona se a aplicação não for dona do schema — dono ignora REVOKE e RLS. A aplicação conecta como portaria_app; o Flyway, como portaria (dono). Ver 02-MODELO-DE-DADOS.md §8.
A.6 — Roubo de token
Mitigação: JWT de visitante com 15 min, escopo de uma visita, atado ao gateId e à sessão — não ao IP de origem: IP de celular muda no meio da sessão (troca de torre, CGNAT) e a amarração derrubaria visitante legítimo no meio do fluxo. A defesa é a posse do token somada à validade curta e ao escopo mínimo. Refresh do morador no Keystore/Keychain com biometria de dispositivo. Rotação de refresh token e revogação por dispositivo no admin.
A.7 — QR vazado ou clonado
Mitigação: gates.qr_version — incrementar invalida todos os QRs anteriores. Reimprime a placa e os códigos antigos param de validar. Não é rotação automática (o QR é impresso), mas é resposta a incidente.
6. Autenticação e autorização
| Perfil | Mecanismo | Sessão |
|---|---|---|
| Visitante | JWT efêmero pós-QR + geofence | 15 min, uma visita |
| Morador | OIDC + refresh no keystore | 15 min / 30 dias |
| Operador | OIDC + refresh | 15 min / 30 dias |
| Admin | OIDC + MFA obrigatório | 8h |
MFA no admin não é opcional: esse perfil vê fotos de todos os visitantes e a localização de todos os acessos. É o alvo mais valioso do sistema.
Autorização com Spring Security por método, sempre com tenant_id no predicado:
@PreAuthorize("@access.canViewVisit(#visitId, authentication)")
fun getVisit(visitId: UUID): VisitDto
Morador vê apenas visitas das suas unidades. Operador vê apenas as que estão ou estiveram na fila. Admin vê todas do seu tenant — e cada acesso a mídia gera linha de auditoria.
7. Segurança da aplicação
Headers no Traefik: HSTS, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, CSP restritiva com frame-ancestors 'none'.
Upload de mídia: Content-Type validado por magic bytes, não por extensão; limite de 8MB para foto e 30MB para vídeo; metadados EXIF removidos no servidor (EXIF carrega GPS próprio, que não é o dado que queremos e não passou pelo aviso); nome de arquivo gerado pelo servidor, nunca o do cliente.
Rate limits: sessão de visitante 5/min por IP e 20/h por dispositivo; criação de visita 3/min por sessão; login admin 5/15min com bloqueio progressivo.
Dependências: Dependabot ativo, gradle dependencyCheck e pnpm audit em CI, build falha em vulnerabilidade alta ou crítica.
8. Contratos e papéis
Condomínio = controlador. Plataforma = operadora. O contrato de operador precisa fixar: finalidades permitidas, proibição de uso secundário, prazos de retenção, obrigação de auxílio no atendimento a titulares, notificação de incidente em até 24h, e destino dos dados no encerramento.
Cada condomínio indica um encarregado (DPO), cadastrado no admin e exibido no aviso de privacidade.
Incidente de segurança: contenção → registro → avaliação de risco → comunicação à ANPD e aos titulares em prazo razoável (a ANPD orienta 2 dias úteis) → relatório final. Runbook em docs/anexos/runbook-incidente.md.
9. Checklist de conformidade
- LIA preenchida e arquivada por condomínio
- Aviso de tratamento visível antes de qualquer coleta, com versão registrada
- Placa física na portaria
- Encarregado indicado e publicado
- Retenção configurada e job de expurgo rodando
- Exportação e eliminação testadas ponta a ponta
audit_logcomUPDATE/DELETErevogados no banco- Nenhuma URL pública de mídia
- Nenhuma biblioteca de reconhecimento facial no
build.gradle.ktsou nopackage.json - Busca cega verificada — inclusive tempo de resposta constante
- Contrato de operador assinado
- Runbook de incidente testado