Conformidade LGPD/GDPR
Sport Tech Club - Lei Geral de Proteção de Dados
Visão Geral
Este documento estabelece as diretrizes e implementações para conformidade com a Lei Geral de Proteção de Dados (LGPD - Lei 13.709/2018) e, quando aplicável, o Regulamento Geral sobre a Proteção de Dados (GDPR) da União Europeia.
1. Princípios de Proteção de Dados
1.1 Princípios LGPD (Art. 6º)
| Princípio | Implementação |
|---|---|
| Finalidade | Dados coletados apenas para propósitos legítimos e informados |
| Adequação | Dados compatíveis com as finalidades declaradas |
| Necessidade | Coleta mínima de dados necessários |
| Livre Acesso | Portal de privacidade para acesso aos dados |
| Qualidade | Mecanismos de atualização de dados pelo titular |
| Transparência | Política de privacidade clara e acessível |
| Segurança | Criptografia, RLS, controles de acesso |
| Prevenção | Auditorias, monitoramento, DPIA |
| Não Discriminação | Sem tratamento discriminatório |
| Responsabilização | Logs de auditoria, DPO designado |
1.2 Bases Legais Utilizadas
yaml
bases_legais:
# Reservas e pagamentos
execucao_contrato:
- cadastro_usuario
- reservas
- pagamentos
- historico_transacoes
# Segurança e fraude
legitimo_interesse:
- logs_seguranca
- deteccao_fraude
- analytics_agregados
# Comunicações
consentimento:
- marketing_email
- marketing_sms
- marketing_push
- compartilhamento_parceiros
# Fiscal e legal
obrigacao_legal:
- notas_fiscais
- dados_fiscais
- retencao_legal2. Inventário de Dados Pessoais
2.1 Dados Coletados
yaml
dados_pessoais:
identificacao:
campos:
- nome_completo
- email
- telefone
- cpf
- data_nascimento
- genero
- foto_perfil
finalidade: Identificação e autenticação
base_legal: Execução de contrato
retencao: Enquanto conta ativa + 5 anos
localizacao:
campos:
- endereco_ip
- geolocalizacao_checkin
- cidade_preferida
finalidade: Funcionalidades baseadas em localização
base_legal: Consentimento / Execução contrato
retencao: 1 ano
financeiros:
campos:
- historico_pagamentos
- metodo_pagamento (tokenizado)
finalidade: Processamento de pagamentos
base_legal: Execução de contrato
retencao: 5 anos (obrigação fiscal)
comportamentais:
campos:
- historico_reservas
- preferencias_esportivas
- nivel_habilidade
- estatisticas_jogos
finalidade: Personalização e gamificação
base_legal: Execução de contrato
retencao: Enquanto conta ativa
sensíveis:
campos:
- dados_saude (opcional)
- restricoes_fisicas (opcional)
finalidade: Segurança na prática esportiva
base_legal: Consentimento explícito
retencao: Até revogação2.2 Mapeamento de Fluxo de Dados
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Usuário │────▶│ Frontend │────▶│ Backend │
│ (Titular) │ │ (Vue.js) │ │ (NestJS) │
└──────────────┘ └──────────────┘ └──────────────┘
│
┌────────────────────────────┼────────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ Keycloak │ │ Storage │
│ (Dados) │ │ (Auth) │ │ (Arquivos) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Backup │ │ Logs │ │ CDN │
│ (AWS S3) │ │ (ELK) │ │ (CloudFront)|
└──────────────┘ └──────────────┘ └──────────────┘3. Direitos dos Titulares
3.1 Implementação de Direitos (Art. 18 LGPD)
typescript
// API de Direitos do Titular
interface PrivacyRightsAPI {
// 1. Confirmação e acesso
GET /api/v1/privacy/data-export
// Retorna todos os dados do titular em formato JSON/CSV
// 2. Correção
PUT /api/v1/privacy/data-correction
// Permite atualização de dados pessoais
// 3. Anonimização/Bloqueio/Eliminação
DELETE /api/v1/privacy/data-deletion
// Processa solicitação de exclusão
// 4. Portabilidade
GET /api/v1/privacy/data-portability
// Exporta dados em formato interoperável
// 5. Revogação de consentimento
DELETE /api/v1/privacy/consents/{type}
// Revoga consentimento específico
// 6. Informações sobre compartilhamento
GET /api/v1/privacy/sharing-info
// Lista entidades com acesso aos dados
}3.2 Service de Privacidade
typescript
@Injectable()
export class PrivacyService {
constructor(
private prisma: PrismaService,
private storageService: StorageService,
private auditService: AuditService,
) {}
/**
* Exporta todos os dados do titular
*/
async exportUserData(userId: string): Promise<DataExport> {
const user = await this.prisma.user.findUnique({
where: { id: userId },
include: {
playerProfile: {
include: {
skillRatings: true,
achievements: true,
gamification: true,
},
},
bookings: {
include: {
payments: true,
participants: true,
},
},
consents: true,
devices: true,
notifications: true,
},
});
// Formata para exportação
const exportData: DataExport = {
exportDate: new Date(),
dataController: 'Sport Tech Club',
subject: {
id: user.id,
email: user.email,
},
personalData: this.formatPersonalData(user),
activityData: this.formatActivityData(user),
consents: this.formatConsents(user.consents),
metadata: {
format: 'JSON',
version: '1.0',
},
};
// Registra auditoria
await this.auditService.log({
action: 'DATA_EXPORT',
userId,
entityType: 'User',
entityId: userId,
});
return exportData;
}
/**
* Processa solicitação de exclusão de dados
*/
async requestDataDeletion(
userId: string,
reason?: string,
): Promise<DeletionRequest> {
// Cria solicitação
const request = await this.prisma.dataDeletionRequest.create({
data: {
userId,
reason,
status: 'PENDING',
requestedAt: new Date(),
scheduledFor: this.calculateDeletionDate(), // 30 dias
},
});
// Notifica DPO
await this.notifyDPO('DATA_DELETION_REQUEST', {
userId,
requestId: request.id,
});
// Registra auditoria
await this.auditService.log({
action: 'DATA_DELETION_REQUEST',
userId,
entityType: 'DeletionRequest',
entityId: request.id,
});
return request;
}
/**
* Executa exclusão de dados
*/
async executeDataDeletion(requestId: string): Promise<void> {
const request = await this.prisma.dataDeletionRequest.findUnique({
where: { id: requestId },
include: { user: true },
});
const userId = request.userId;
// 1. Anonimiza dados que devem ser retidos
await this.anonymizeRetainedData(userId);
// 2. Remove dados que podem ser deletados
await this.deleteRemovableData(userId);
// 3. Remove de serviços externos
await this.deleteFromExternalServices(userId);
// 4. Atualiza status da solicitação
await this.prisma.dataDeletionRequest.update({
where: { id: requestId },
data: {
status: 'COMPLETED',
completedAt: new Date(),
},
});
// 5. Registra auditoria
await this.auditService.log({
action: 'DATA_DELETION_COMPLETED',
entityType: 'DeletionRequest',
entityId: requestId,
});
}
/**
* Anonimiza dados que devem ser retidos por obrigação legal
*/
private async anonymizeRetainedData(userId: string): Promise<void> {
// Anonimiza reservas (retenção fiscal de 5 anos)
await this.prisma.booking.updateMany({
where: { userId },
data: {
metadata: {
anonymized: true,
anonymizedAt: new Date().toISOString(),
},
},
});
// Anonimiza pagamentos
await this.prisma.payment.updateMany({
where: { booking: { userId } },
data: {
metadata: {
anonymized: true,
anonymizedAt: new Date().toISOString(),
},
},
});
}
/**
* Remove dados que podem ser completamente deletados
*/
private async deleteRemovableData(userId: string): Promise<void> {
// Remove em ordem de dependência
await this.prisma.$transaction([
this.prisma.notification.deleteMany({ where: { userId } }),
this.prisma.userDevice.deleteMany({ where: { userId } }),
this.prisma.userConsent.deleteMany({ where: { userId } }),
this.prisma.playerAchievement.deleteMany({
where: { playerProfile: { userId } },
}),
this.prisma.skillRating.deleteMany({
where: { playerProfile: { userId } },
}),
this.prisma.playerGamification.deleteMany({
where: { playerProfile: { userId } },
}),
this.prisma.playerProfile.deleteMany({ where: { userId } }),
this.prisma.userRole.deleteMany({ where: { userId } }),
// Anonimiza usuário ao invés de deletar
this.prisma.user.update({
where: { id: userId },
data: {
email: `deleted_${userId}@anonymized.local`,
firstName: 'Usuário',
lastName: 'Removido',
cpf: null,
phone: null,
avatarUrl: null,
status: 'DELETED',
deletedAt: new Date(),
},
}),
]);
}
}4. Consentimento
4.1 Gestão de Consentimentos
typescript
// Tipos de consentimento
enum ConsentType {
TERMS_OF_SERVICE = 'TERMS_OF_SERVICE',
PRIVACY_POLICY = 'PRIVACY_POLICY',
MARKETING_EMAIL = 'MARKETING_EMAIL',
MARKETING_SMS = 'MARKETING_SMS',
MARKETING_PUSH = 'MARKETING_PUSH',
DATA_PROCESSING = 'DATA_PROCESSING',
}
// Registro de consentimento
interface ConsentRecord {
id: string;
userId: string;
consentType: ConsentType;
version: string;
granted: boolean;
ipAddress: string;
userAgent: string;
grantedAt: Date;
revokedAt?: Date;
}
@Injectable()
export class ConsentService {
/**
* Registra consentimento do usuário
*/
async grantConsent(
userId: string,
consentType: ConsentType,
version: string,
context: RequestContext,
): Promise<ConsentRecord> {
// Revoga versão anterior
await this.prisma.userConsent.updateMany({
where: {
userId,
consentType,
revokedAt: null,
},
data: {
revokedAt: new Date(),
},
});
// Cria novo registro
const consent = await this.prisma.userConsent.create({
data: {
userId,
consentType,
version,
granted: true,
ipAddress: context.ipAddress,
userAgent: context.userAgent,
grantedAt: new Date(),
},
});
// Auditoria
await this.auditService.log({
action: 'CONSENT_GRANTED',
userId,
entityType: 'Consent',
entityId: consent.id,
newValues: { consentType, version },
});
return consent;
}
/**
* Revoga consentimento
*/
async revokeConsent(
userId: string,
consentType: ConsentType,
): Promise<void> {
await this.prisma.userConsent.updateMany({
where: {
userId,
consentType,
revokedAt: null,
},
data: {
revokedAt: new Date(),
},
});
// Processa implicações da revogação
await this.handleConsentRevocation(userId, consentType);
// Auditoria
await this.auditService.log({
action: 'CONSENT_REVOKED',
userId,
entityType: 'Consent',
metadata: { consentType },
});
}
/**
* Verifica se usuário tem consentimento ativo
*/
async hasConsent(
userId: string,
consentType: ConsentType,
): Promise<boolean> {
const consent = await this.prisma.userConsent.findFirst({
where: {
userId,
consentType,
granted: true,
revokedAt: null,
},
});
return !!consent;
}
}4.2 UI de Consentimento
vue
<template>
<div class="consent-modal">
<h2>Gerenciar Preferências de Privacidade</h2>
<div class="consent-section required">
<h3>Necessários</h3>
<p>Estes são essenciais para o funcionamento do serviço.</p>
<div class="consent-item">
<input type="checkbox" checked disabled />
<label>
<strong>Termos de Uso</strong>
<span>Aceito os termos de uso do Sport Tech Club</span>
</label>
</div>
<div class="consent-item">
<input type="checkbox" checked disabled />
<label>
<strong>Política de Privacidade</strong>
<span>Li e concordo com a política de privacidade</span>
</label>
</div>
</div>
<div class="consent-section optional">
<h3>Opcionais</h3>
<p>Você pode alterar estas preferências a qualquer momento.</p>
<div class="consent-item">
<input
type="checkbox"
v-model="consents.marketingEmail"
@change="updateConsent('MARKETING_EMAIL')"
/>
<label>
<strong>E-mails promocionais</strong>
<span>Receber ofertas e novidades por e-mail</span>
</label>
</div>
<div class="consent-item">
<input
type="checkbox"
v-model="consents.marketingSms"
@change="updateConsent('MARKETING_SMS')"
/>
<label>
<strong>SMS promocional</strong>
<span>Receber ofertas por SMS</span>
</label>
</div>
<div class="consent-item">
<input
type="checkbox"
v-model="consents.marketingPush"
@change="updateConsent('MARKETING_PUSH')"
/>
<label>
<strong>Notificações push promocionais</strong>
<span>Receber ofertas via notificações</span>
</label>
</div>
</div>
<div class="consent-actions">
<button @click="saveAll" class="btn-primary">Salvar Preferências</button>
<router-link to="/privacy/data-export">Exportar meus dados</router-link>
<router-link to="/privacy/delete-account" class="danger">
Solicitar exclusão da conta
</router-link>
</div>
</div>
</template>5. Segurança de Dados
5.1 Criptografia
yaml
criptografia:
em_repouso:
banco_dados:
metodo: AES-256-GCM
gerenciamento_chaves: AWS KMS
rotacao: 90 dias
backups:
metodo: AES-256-GCM
chave_separada: true
arquivos:
metodo: AES-256-GCM
por_tenant: true
em_transito:
protocolo: TLS 1.3
certificados: Let's Encrypt
rotacao: 30 dias
campos_sensiveis:
cpf:
tipo: mascaramento
formato: "***.***.***-XX"
email:
tipo: criptografia
algoritmo: AES-256-GCM
telefone:
tipo: criptografia
algoritmo: AES-256-GCM
cartao_credito:
tipo: tokenizacao
provedor: Gateway de pagamento5.2 Implementação de Campo Criptografado
typescript
// Decorator para campos criptografados
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
const ALGORITHM = 'aes-256-gcm';
const KEY = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
export function encrypt(text: string): string {
const iv = randomBytes(16);
const cipher = createCipheriv(ALGORITHM, KEY, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
}
export function decrypt(encryptedText: string): string {
const [ivHex, authTagHex, encrypted] = encryptedText.split(':');
const iv = Buffer.from(ivHex, 'hex');
const authTag = Buffer.from(authTagHex, 'hex');
const decipher = createDecipheriv(ALGORITHM, KEY, iv);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// Prisma middleware para criptografia automática
export const encryptionMiddleware: Prisma.Middleware = async (params, next) => {
const encryptedFields = {
User: ['phone'],
// Adicionar outros modelos/campos conforme necessário
};
const modelFields = encryptedFields[params.model];
if (modelFields) {
// Criptografa na escrita
if (['create', 'update', 'upsert'].includes(params.action)) {
for (const field of modelFields) {
if (params.args.data?.[field]) {
params.args.data[field] = encrypt(params.args.data[field]);
}
}
}
}
const result = await next(params);
// Descriptografa na leitura
if (modelFields && result) {
const decryptResult = (obj: any) => {
for (const field of modelFields) {
if (obj?.[field]) {
try {
obj[field] = decrypt(obj[field]);
} catch {
// Campo não criptografado (legado)
}
}
}
};
if (Array.isArray(result)) {
result.forEach(decryptResult);
} else {
decryptResult(result);
}
}
return result;
};6. Retenção de Dados
6.1 Política de Retenção
yaml
retencao_dados:
# Dados de conta ativa
conta_ativa:
dados_pessoais: Enquanto ativa
historico_reservas: 2 anos
historico_pagamentos: 5 anos (fiscal)
logs_acesso: 1 ano
# Após exclusão de conta
conta_deletada:
dados_anonimizados: 5 anos (fiscal)
dados_pessoais: Imediato
logs_auditoria: 1 ano
# Dados operacionais
operacionais:
logs_sistema: 90 dias
logs_erro: 1 ano
metricas: 2 anos (agregado)
backups: 30 dias
# Dados de marketing
marketing:
consentimentos: Enquanto válido + 5 anos
historico_campanhas: 2 anos
preferencias: Até revogação6.2 Job de Limpeza
typescript
@Injectable()
export class DataRetentionService {
constructor(
private prisma: PrismaService,
private auditService: AuditService,
) {}
@Cron('0 3 * * *') // Diariamente às 3:00
async cleanupExpiredData(): Promise<void> {
const startTime = Date.now();
const results = {
bookings: 0,
logs: 0,
notifications: 0,
};
// 1. Anonimiza reservas antigas (> 2 anos)
const twoYearsAgo = new Date();
twoYearsAgo.setFullYear(twoYearsAgo.getFullYear() - 2);
const bookingsToAnonymize = await this.prisma.booking.findMany({
where: {
createdAt: { lt: twoYearsAgo },
metadata: {
path: ['anonymized'],
equals: Prisma.DbNull,
},
},
take: 1000, // Batch de 1000
});
for (const booking of bookingsToAnonymize) {
await this.anonymizeBooking(booking.id);
results.bookings++;
}
// 2. Remove logs antigos (> 90 dias)
const ninetyDaysAgo = new Date();
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
const deletedLogs = await this.prisma.systemLog.deleteMany({
where: {
createdAt: { lt: ninetyDaysAgo },
},
});
results.logs = deletedLogs.count;
// 3. Remove notificações lidas antigas (> 30 dias)
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const deletedNotifications = await this.prisma.notification.deleteMany({
where: {
readAt: { lt: thirtyDaysAgo },
},
});
results.notifications = deletedNotifications.count;
// Registra execução
await this.auditService.log({
action: 'DATA_RETENTION_CLEANUP',
entityType: 'System',
metadata: {
duration: Date.now() - startTime,
results,
},
});
}
}7. Incidentes de Segurança
7.1 Plano de Resposta
yaml
resposta_incidentes:
classificacao:
critico: # Vazamento de dados pessoais
prazo_notificacao: 72 horas
notificar:
- ANPD
- Titulares afetados
- DPO
- Diretoria
alto: # Tentativa de acesso não autorizado
prazo_notificacao: 24 horas
notificar:
- DPO
- Time de segurança
medio: # Anomalia detectada
prazo_notificacao: 48 horas
notificar:
- DPO
baixo: # Eventos de auditoria
prazo_notificacao: 7 dias
notificar:
- Relatório semanal
processo:
1_deteccao:
- Monitoramento automatizado
- Alertas de segurança
- Reports de usuários
2_contenção:
- Isolar sistemas afetados
- Bloquear acessos suspeitos
- Preservar evidências
3_erradicação:
- Identificar causa raiz
- Corrigir vulnerabilidade
- Atualizar sistemas
4_recuperação:
- Restaurar serviços
- Validar integridade
- Monitorar recorrência
5_pós_incidente:
- Documentar lições
- Atualizar procedimentos
- Treinar equipe7.2 Notificação à ANPD
typescript
interface ANPDNotification {
// Identificação do controlador
controlador: {
razaoSocial: string;
cnpj: string;
encarregado: {
nome: string;
email: string;
telefone: string;
};
};
// Descrição do incidente
incidente: {
dataOcorrencia: Date;
dataConhecimento: Date;
natureza: string; // 'Vazamento', 'Acesso não autorizado', etc.
descricao: string;
dadosAfetados: string[];
volumeAfetados: number;
};
// Medidas adotadas
medidas: {
contencao: string[];
mitigacao: string[];
comunicacaoTitulares: boolean;
dataComunicacao?: Date;
};
// Avaliação de risco
risco: {
probabilidadeDano: 'Baixa' | 'Média' | 'Alta';
gravidadeDano: 'Baixa' | 'Média' | 'Alta';
justificativa: string;
};
}8. Transferência Internacional
8.1 Serviços Utilizados
yaml
transferencia_internacional:
provedores:
aws:
pais: EUA
dados: Backups, infraestrutura
base_legal: Cláusulas contratuais padrão
certificacoes:
- SOC 2
- ISO 27001
stripe:
pais: EUA
dados: Tokens de pagamento
base_legal: Privacy Shield (substituído por SCCs)
certificacoes:
- PCI DSS Level 1
sendgrid:
pais: EUA
dados: Emails (endereço + conteúdo)
base_legal: Cláusulas contratuais padrão
garantias:
- Cláusulas contratuais padrão (SCCs)
- Avaliações de impacto de transferência
- Criptografia em trânsito e repouso
- Contratos de processamento de dados (DPA)9. DPO e Governança
9.1 Encarregado de Dados (DPO)
yaml
dpo:
nome: "[Nome do DPO]"
email: dpo@sporttechclub.com
telefone: "+55 (11) XXXX-XXXX"
responsabilidades:
- Orientar sobre obrigações LGPD
- Receber reclamações de titulares
- Interagir com ANPD
- Supervisionar auditorias
- Aprovar DPIA
- Treinar colaboradores
canal_contato:
url: https://sporttechclub.com/privacidade
formulario: https://sporttechclub.com/contato-dpo
prazo_resposta: 15 dias9.2 Comitê de Privacidade
yaml
comite_privacidade:
membros:
- DPO (Coordenador)
- CTO
- Jurídico
- Segurança da Informação
- Produto
reunioes:
frequencia: Mensal
pauta:
- Status de conformidade
- Incidentes reportados
- Solicitações de titulares
- Novos projetos (Privacy by Design)
- Atualizações regulatórias
decisoes:
- DPIA para novos projetos
- Aprovação de terceiros
- Políticas de privacidade
- Resposta a incidentes10. Checklist de Conformidade
10.1 Documentação
- [ ] Política de Privacidade publicada
- [ ] Termos de Uso atualizados
- [ ] Registro de atividades de tratamento
- [ ] DPIAs realizados para processos de alto risco
- [ ] Contratos com operadores (DPAs)
- [ ] Procedimento de resposta a incidentes
10.2 Técnico
- [ ] Criptografia em repouso implementada
- [ ] Criptografia em trânsito (TLS 1.3)
- [ ] Row Level Security (RLS) ativo
- [ ] Logs de auditoria funcionando
- [ ] Backup criptografado
- [ ] Portal de privacidade disponível
- [ ] API de direitos do titular implementada
10.3 Organizacional
- [ ] DPO designado
- [ ] Comitê de privacidade ativo
- [ ] Treinamento de colaboradores realizado
- [ ] Procedimentos documentados
- [ ] Canal de contato disponível
- [ ] Métricas de privacidade monitoradas