Skip to content

Procedimiento de Retención y Eliminación Segura de Datos ​

Documento: DOC-DATA-RET-01 Versión: 1.1 (2026-08-07 — retention table explícita + status implementación real) Fecha de aprobación: 2026-07-29 Owner: Security Lead + DPO Frecuencia de revisión: Anual + tras cambios significativos en el CDE Referencias PCI DSS: Req 3.2.1, Req 3.3.1, Req 9.4 Cuestionario ControlCase: Pregunta 25


0. Tabla explícita de retención (autoritativa para QSA) ​

Esta tabla es la fuente única de verdad de tiempos de retención por tipo de dato. Todo componente automatizado (cron, lifecycle policy, sweeper) debe cumplir estos valores.

#Tipo de datoUbicación primariaTiempo de retención (explícito)Trigger de eliminaciónMecanismoEstado
1PAN cifrado (envelope AES-256-GCM)Postgres card_vault.stored_cards30 días desde deleted_at post-soft-deleteCron diario 03:00 UTCPanPurgeService (NestJS @Cron) + VACUUM FULL✅ IMPLEMENTADO (evidencia logs 6 días consecutivos)
2Nombre del portador (cifrado)Postgres card_vault.stored_cards.cardholder_name_ciphertextIgual que PAN (30 días post soft-delete)Mismo cron que PANPanPurgeService (misma tabla)✅ IMPLEMENTADO
3Fecha de expiración (cifrada)Postgres card_vault.stored_cards.exp_*_ciphertextIgual que PANMismo cronMismo✅ IMPLEMENTADO
4PAN fingerprint (HMAC-SHA256)Postgres card_vault.stored_cards.pan_fingerprintIgual que PAN (misma fila)Mismo cronMismo✅ IMPLEMENTADO
5PAN truncado (BIN + last4)Postgres payments_core.payment_intents.card_display7 años (retención fiscal Colombia — Estatuto Tributario Art. 632)Job anual (Q4)Manual + auditado📋 Programado 2026-Q4
6CVV / CVV2 (SAD)Vault kv-v2 secret/sad/* (RAM del proceso)Max 15 minutos (TTL Vault kv-v2) + purge inmediato post-auth(a) purgeAfterAuth post-response del processor · (b) Sweeper cada 60sSadPurgeService.purgeAfterAuth + sweeper✅ IMPLEMENTADO (post-auth) · ⚠️ Sweeper: bug de policy corregido 2026-08-07 (SEC-042)
7PIN / PIN BlockN/A — NO recibidoN/AN/AN/A🚫 No aplica (card-not-present only)
8Track 1 / Track 2N/A — NO recibidoN/AN/AN/A🚫 No aplica (no card-present)
9Backups de Postgres (GPG encrypted)Backblaze B2 s3://fintrix-dr-postgres/daily/7 años (bucket Object Lock Compliance)Bucket lifecycle policyB2 Object Lock Compliance (immutable)✅ IMPLEMENTADO (CronJob pg-dump-b2 diario)
10Snapshots de Vault RaftBackblaze B2 s3://fintrix-dr-vault/7 años (Object Lock Compliance)Bucket lifecycleSystemd timer vault-snapshot-b2.timer + Object Lock✅ IMPLEMENTADO
11Vault audit log (append-only)Backblaze B2 s3://fintrix-dr-vault-audit/3 años (Object Lock Compliance)Bucket lifecyclevault-audit-uploader.sh + Object Lock✅ IMPLEMENTADO
12Logs de aplicación PCI (Wazuh SIEM)Wazuh cluster + B2 archive1 año (PCI DSS Req 10.5.1 mínimo) — 90 días hot + 275 días coldWazuh retention config + B2 lifecycleWazuh + B2✅ IMPLEMENTADO
13pgAudit logs (DB events)Postgres + B21 año hot / 3 años coldpg-audit-log-shipper (daily) + B2Systemd timer + B2✅ IMPLEMENTADO
14K8s state (etcd snapshots)B2 s3://fintrix-dr-k8s-state/90 díasCronJob k8s-state-b2 diarioK8s CronJob✅ IMPLEMENTADO
15JWT signing keys (RS256)Vault secret/jwt/*Rotación 6 mesesRotación programadaVault + K8s deployment restart📋 Rotación pendiente 2026-Q4
16API keys de merchant (SHA-256 hash)Postgres payments_core.api_keysVida activa del API keyMerchant revoca desde dashboardUPDATE revoked_at + DELETE 30d después📋 Endpoint activo, sin API keys creadas aún
17Email/nombre de merchant (usuarios auth)Postgres auth_db.usersVida de cuenta + 30 días post-bajaJob manual + triggerManual auditado✅ IMPLEMENTADO
18Sesiones JWT (in-memory)RAM (JWT stateless)1 hora TTLExpiración natural JWTN/A (no storage)✅ IMPLEMENTADO
19Media físico decomisionadoN/A (no on-prem — solo cloud managed)N/A — DO garantiza wipe post-destroyDO managedDO NIST 800-88 (contractual)✅ IMPLEMENTADO (VND-2026-005 attest)

Notas críticas:

  • Todos los tiempos son absolutos, no "aproximados". Cumplimiento verificable con SELECT max(now() - created_at) FROM <tabla>.
  • Cambios a esta tabla requieren aprobación CTO + DPO (bitácora en §9).
  • El campo "Estado" refleja la implementación real verificada al 2026-08-07 con evidencia en qsa-responses/EVD-Q25-EXECUTION-EVIDENCE.

1. Propósito ​

Definir el procedimiento operativo y automatizado para (a) mantener la retención de datos protegidos al mínimo necesario y (b) eliminarlos de forma segura al final de su ciclo de vida.

2. Alcance ​

Aplica a todos los datos clasificados en COVERED-INFORMATION-DATA-MATRIX.md, incluyendo:

  • CHD (PAN, cardholder name, exp) almacenado en card_vault.stored_cards
  • SAD (CVV) en RAM del tokenization-service
  • Backups de Postgres
  • Logs de aplicación e infraestructura
  • Snapshots de Vault
  • Media físico decomisionado

3. Principios ​

  1. Mínima retención: solo se retiene el dato mientras exista justificación de negocio activa
  2. Purge irreversible: eliminación cripto o físicamente irreversible (no soft delete)
  3. Auditabilidad: toda ejecución del purge genera evento en audit log inmutable
  4. Verificabilidad: el QSA puede reproducir el conteo de datos borrados en cualquier momento

4. Retenciones definidas ​

Ver COVERED-INFORMATION-DATA-MATRIX.md §7 para la tabla autoritativa.

5. Procedimientos de eliminación ​

5.1 SAD (CVV) — purge continuo ​

Frecuencia: cada 60 segundos + inmediato post-autorización.

Mecanismo dual:

  1. Immediate purge post-authorization

    • tokenization-service recibe respuesta del payment processor
    • Independiente del resultado (aprobada/declinada/error) → invalida tok_cvv_* inmediatamente
    • Emite evento Kafka fintrix.security.sad.purged con token_id, reason=post_auth, timestamp
  2. Cron sweeper (safety net)

    • Cronjob sad-purge-sweeper corre cada 60 seg
    • Query Vault kv-v2: lista tokens tok_cvv_* con lease > 15 min
    • Ejecuta vault kv delete secret/data/sad/{token_id}
    • Emite evento Kafka fintrix.security.sad.purged con reason=ttl_sweep

Ubicación del código: backend/apps/tokenization-service/src/sad-purge/sad-purge.service.ts — SEC-019 ✅ IMPLEMENTADO 2026-07-30 (verificado en producción con pods tokenization-service-* en namespace pci-cde).

Evidencia auditable:

  • Vault audit log: entries type=revoke path=sad/*
  • Kafka topic fintrix.security.sad.purged con retention 7 años
  • Dashboard Grafana SAD Purge Metrics con eventos/hora

5.2 PAN de tarjetas canceladas — purge diario ​

Frecuencia: diaria, 03:00 UTC (baja demanda).

Trigger: una tarjeta pasa a deleted_at IS NOT NULL cuando:

  • El cliente elimina el payment method desde el dashboard
  • La suscripción asociada se cancela y no queda otra suscripción activa con esa tarjeta
  • El fraud team marca la tarjeta como comprometida

Ventana de gracia: 30 días desde deleted_at (para chargeback disputes).

Cronjob: pan-purge-canceled-cards

sql
-- Ejecutado por el cronjob (audit-logged):
BEGIN;
  DELETE FROM card_vault.stored_cards
  WHERE deleted_at IS NOT NULL
    AND deleted_at < now() - interval '30 days'
  RETURNING id, deleted_at;
  -- rows returned se loguean con SHA-256 hash antes de commit
COMMIT;

-- Post-commit:
VACUUM FULL card_vault.stored_cards;
-- Fuerza reescritura del tablespace → dato en filesystem se sobrescribe

Ubicación del código: backend/apps/card-vault-service/src/purge/pan-purge.service.ts — SEC-020 ✅ IMPLEMENTADO 2026-07-30 (verificado con 6 días consecutivos de ejecución en producción, ver EVD-Q25-EXECUTION-EVIDENCE).

Evidencia auditable:

  • Postgres audit log (pgAudit extension) con DELETE statements + row counts
  • Kafka topic fintrix.security.pan.purged con card_id_hash, purged_at, days_since_deletion
  • Job artifact en Kubernetes: kubectl -n pci-cde logs job/pan-purge-canceled-cards-<date>

5.3 Backups >90 días ​

Frecuencia: automática vía DO Spaces object lifecycle policy.

Policy configurada en bucket fintrix-db-backup-nyc3:

json
{
  "Rules": [
    {
      "ID": "PurgeBackupsOlderThan90Days",
      "Status": "Enabled",
      "Filter": { "Prefix": "postgres/" },
      "Expiration": { "Days": 90 }
    }
  ]
}

Verificación:

bash
# Listar backups actuales con edad
s3cmd ls s3://fintrix-db-backup-nyc3/postgres/ | awk '{print $1, $NF}'
# Alerta si se encuentra archivo > 90 días

5.4 Logs >365 días ​

Frecuencia: automática vía Loki retention + Spaces lifecycle.

  • Loki: retention_period: 2160h (90 días online)
  • DO Spaces archive: lifecycle policy con expiration a 275 días adicionales
  • Total: 365 días máximo, cumple PCI DSS Req 10.5.1

5.5 Media físico decomisionado ​

Aplica si un droplet, disco físico, o dispositivo sale del CDE.

Procedimiento (MEDIA-DISPOSAL-RUNBOOK.md):

  1. Backup pre-decomiso (si aún se necesita el dato)
  2. Wipe con shred -uvfz -n 3 /dev/sdX o equivalente
  3. Overwrite verificable con patterns aleatorios (mínimo 3 pases)
  4. Para SSD: blkdiscard + verificación de erase con hdparm --security-erase
  5. Destrucción física del disco (drill o triturado industrial) por vendor certificado
  6. Certificate of Destruction firmado por el vendor
  7. Archivar CoD en drive legal + evento fintrix.security.media.destroyed a SIEM

Vendor autorizado en Colombia: [TO_DEFINE] (Iron Mountain o similar)

5.6 Vault snapshots ​

Retención: 30 días de snapshots diarios + 12 snapshots mensuales (para RTO/RPO).

Cronjob en el cluster Vault:

bash
# Diario 04:00 UTC
vault operator raft snapshot save /var/backups/vault-$(date +%Y%m%d).snap
# Upload a Spaces WORM (immutable)
s3cmd put /var/backups/vault-*.snap s3://fintrix-vault-audit-nyc3/snapshots/
# Purge locales > 7 días
find /var/backups/vault-*.snap -mtime +7 -delete
# Spaces lifecycle purga > 30 días automáticamente

6. Verificación de ejecución (evidencia auditable) ​

Todo purge debe generar evidencia verificable por el QSA. Las evidencias se archivan en:

PurgeEvidencia primariaUbicaciónRetención evidencia
SAD sweeperVault audit log entriess3://fintrix-vault-audit-nyc3/audit/ (WORM)3 años
PAN canceledPostgres pgAudit + Kafka eventLoki + Kafka topic7 años
Backup lifecycleSpaces access logss3://fintrix-logs-archive-nyc3/spaces-access/1 año
Logs retentionLoki retention metricsPrometheus loki_ingester_streams_removed_total1 año
Media disposalCertificate of Destruction (PDF)Drive legal + escrow notarialVida del sistema
Vault snapshotsS3 access log + snapshot listSpaces WORM3 años

7. Roles y responsabilidades ​

RolResponsabilidad
Security LeadAprobar cambios al procedimiento, revisar evidencia mensualmente
DPOVerificar cumplimiento GDPR/Ley 1581 (retención = minimización)
DevOpsMantener cronjobs, monitorear alerts de purge fallido
DBAEjecutar VACUUM FULL, verificar libertad de dead tuples
QSA (externo)Verificar evidencia trimestralmente y en assessment anual

8. Métricas y alertas ​

Dashboard Grafana: PCI Data Retention Compliance

MétricaAlertaEscalación
sad_tokens_older_than_15_min> 0 durante > 5 minPagerDuty → Security oncall
pan_purge_job_last_success_timestamp> 26 h agoPagerDuty → DevOps oncall
backup_older_than_90_days_count> 0Email → DevOps
logs_older_than_365_days_count> 0Email → DevOps
vault_snapshot_last_uploaded_timestamp> 26 h agoPagerDuty → Security

9. Runbook para failed purge ​

Si un purge falla (ej: constraint violation, deadlock, Vault sealed):

  1. Alerta dispara → oncall recibe
  2. Oncall revisa logs del cronjob: kubectl -n pci-cde logs job/{name}
  3. Diagnóstico común:
    • Vault sealed → escalar a custodios para unseal ceremony
    • deadlock detected → identificar tx en conflicto + retry manual
    • pg connection refused → verificar salud del Postgres primary
  4. Ejecutar purge manual con audit trail:
    bash
    kubectl -n pci-cde create job --from=cronjob/pan-purge-canceled-cards \
      pan-purge-manual-$(date +%Y%m%d-%H%M)
  5. Documentar incidente en INCIDENTS/YYYY-MM-DD-purge-failure.md
  6. Post-mortem obligatorio si downtime > 24 h

10. Testing anual ​

Cada Q4 (previo a la re-certificación):

  1. Test de purge: insertar 100 registros dummy con deleted_at = now() - interval '31 days', ejecutar cronjob manual, verificar 0 rows post-ejecución
  2. Test de restauración de backup: restaurar snapshot de hace 30 días a cluster de staging, verificar integridad
  3. Test de recuperación Vault: unseal ceremony completa desde snapshot de Spaces
  4. Test de shred: wipe de disco de prueba + intento de recovery con photorec — debe fallar

Resultados archivados en docs/pci-dss/annual-tests/YYYY/.

11. Aprobaciones ​

RolNombreFirmaFecha
CTO[TO_FILL]
Security Lead[TO_FILL]
DPO[TO_FILL]

Documentación Confidencial — Solo para uso interno y auditoría PCI DSS