[CONSEJO — Doc 10] Contrarian: BookMind tiene el perfil de seguridad más exigente del portfolio Cheryx porque los datos financieros son más valiosos que los datos de inventario. Un competidor que acceda a los estados financieros de un cliente de BookMind puede conocer exactamente sus márgenes, sus clientes más importantes y su capacidad de endeudamiento. La seguridad de BookMind debe ser de nivel bancario, no de nivel SaaS genérico. Auditor Seg.: Las credenciales de acceso a Hacienda CR, SAT MX y DIAN CO deben tratarse como secretos de alto valor. Si son comprometidas, un atacante puede emitir facturas fraudulentas a nombre del cliente. pgcrypto + key rotation trimestral es el mínimo aceptable. Executor: La inmutabilidad del GL no es solo un principio contable — es un control de seguridad. Si la base de datos tiene
REVOKE UPDATE/DELETEenbk_asientos, incluso un atacante que comprometió la app no puede alterar los registros históricos. La inmutabilidad es la primera línea de defensa contra fraude interno.
1. Modelo de amenazas (STRIDE)
| Amenaza | Vector | Control |
|---|---|---|
| Spoofing | Acceso no autorizado a credenciales FE | pgcrypto + rotación trimestral + acceso solo desde FEService |
| Tampering | Modificación de asientos contables | REVOKE UPDATE/DELETE en bk_asientos + audit log inmutable |
| Repudiation | Cliente niega haber emitido una FE | bk_fe_documents con XML original almacenado + clave Hacienda inapelable |
| Information Disclosure | Cross-tenant leakage de datos financieros | RLS en todas las tablas bk_* + test obligatorio cross-tenant |
| Denial of Service | Rate limiting en emisión FE (evitar spam) | Rate limit: max 100 FE/min por tenant |
| Elevation of Privilege | Contador accede a empresas fuera de su cartera | RBAC + whitelisting de empresas por CPA account |
2. Protección de credenciales FE
Las credenciales de Hacienda CR, SAT MX y DIAN CO son los secretos más críticos del sistema.
2.1 Almacenamiento
-- Almacenamiento encriptado con pgcrypto
INSERT INTO bk_integraciones (tenant_id, empresa_id, pais, tipo, credenciales_enc)
VALUES (
:tenant_id, :empresa_id, 'CR', 'hacienda_cr',
pgp_sym_encrypt(
:credenciales_json, -- {"username": "...", "password": "...", "pin": "..."}
current_setting('app.encryption_key') -- key en Doppler, nunca en código
)
);
-- Lectura (solo FEService)
SELECT pgp_sym_decrypt(credenciales_enc, current_setting('app.encryption_key'))::json
FROM bk_integraciones
WHERE tenant_id = :tenant_id AND pais = 'CR';
2.2 Rotación de la encryption key
- Rotación trimestral de
app.encryption_key(Doppler secret rotation). - Al rotar la key, re-encriptar todos los registros
bk_integracionesen una migración de mantenimiento fuera de horario peak. - Nunca almacenar la key en el repositorio ni en variables de entorno del sistema operativo.
2.3 Acceso a nivel de base de datos
-- Solo fe_service_role puede leer bk_integraciones
REVOKE SELECT ON bk_integraciones FROM app_role;
GRANT SELECT ON bk_integraciones TO fe_service_role;
-- app_role (la app FastAPI general) NO puede leer credenciales
-- Esto limita la superficie de ataque: incluso un endpoint comprometido no expone credenciales FE
3. Inmutabilidad contable como control de seguridad
-- Enforced a nivel de base de datos, no solo en la app
REVOKE UPDATE ON bk_asientos FROM app_role;
REVOKE DELETE ON bk_asientos FROM app_role;
REVOKE UPDATE ON bk_asiento_lineas FROM app_role;
REVOKE DELETE ON bk_asiento_lineas FROM app_role;
REVOKE UPDATE ON bk_fe_documents FROM app_role;
REVOKE DELETE ON bk_movimientos_bancarios FROM app_role;
Corrección de errores sin UPDATE: Si un asiento tiene un error, la corrección se hace con un contra-asiento (nuevo asiento con valores invertidos + referencia al asiento original). Esto es el estándar contable correcto y además es el control de seguridad.
4. RBAC BookMind
| Rol | Descripción | Permisos clave |
|---|---|---|
| Owner | Dueño de la empresa | Todo |
| Gerente Financiero | CFO, gerente interno | GL, AR/AP, bancos, tesorería — NO configuración de credenciales FE |
| Contador CPA | Contador externo | GL completo, cierre mensual, estados financieros — NO acceso a cuentas bancarias completas |
| Asistente Contable | Empleado de operaciones | Emitir FE, registrar facturas recibidas — NO GL manual, NO bancos |
| Cheryx Admin | Soporte interno Cheryx | Read-only para soporte, auditado |
4.1 Control de período cerrado
async def crear_asiento(payload: AsientoPayload, tenant_id: UUID, user_role: str):
if payload.fecha < ultimo_periodo_cerrado:
if user_role not in ['owner', 'contador_cpa']:
raise PermissionError("El período ya fue cerrado. Solo el contador puede registrar asientos en períodos cerrados.")
# proceder con la creación
5. Compliance regulatorio FE
5.1 Costa Rica — Hacienda DGT
| Requisito | Implementación |
|---|---|
| Versión XML | FE v4.4 (Resolución MH-DGT-RES-0027-2024) |
| Firma digital | SHA-256 con certificado BCCR (Banco Central de Costa Rica) |
| Envío | HTTPS a api.hacienda.go.cr — timeout 30s, retry hasta 24h |
| Almacenamiento | XML completo (enviado + respuesta) en bk_fe_documents.xml_enviado y xml_respuesta |
| Retención | 5 años mínimo (Artículo 52 Código de Normas Tributarias CR) |
| Monitoreo de normativa | Suscripción a boletines hacienda.go.cr + revisión mensual |
5.2 México — SAT CFDI 4.0
| Requisito | Implementación |
|---|---|
| Versión | CFDI 4.0 (obligatorio desde enero 2022) |
| Timbrado | Via PAC autorizado (Edicom, Finkok, o equivalente) |
| Complementos | Pago 2.0 para facturas cobradas posteriormente |
| RFC validación | Validación del RFC receptor contra LCO (Lista de Contribuyentes Obligados) antes de emitir |
| Cancelación | Requiere aceptación del receptor (proceso asíncrono vía portal SAT) |
5.3 Colombia — DIAN FE
| Requisito | Implementación |
|---|---|
| Versión | UBL 2.1 (DIAN Resolution 000042/2020) |
| Habilitador | Via Habilitador Tecnológico certificado DIAN |
| CUFE | Código Único de Factura Electrónica — generado y almacenado |
| NIT validación | Validación de NIT receptor antes de emitir |
| Notas crédito/débito | Implementadas como documentos separados con referencia a la FE original |
6. Ley 8968 PRODHAB (Costa Rica)
BookMind como plataforma contable procesa datos de clientes finales de la empresa (sus deudores en AR). Esto tiene implicaciones adicionales:
| Dato | Quién es Responsable | Quién es Encargado |
|---|---|---|
| Datos de usuarios de BookMind (dueño, contador) | Cheryx Group | — |
| Datos de los clientes de la empresa (AR: nombre, cédula, email) | El tenant (empresa) | BookMind/Cheryx |
| Datos financieros del tenant | El tenant | BookMind/Cheryx |
Implicación práctica: el tenant es el Responsable de los datos de sus clientes en AR. BookMind es el Encargado. El DPA (Data Processing Agreement) debe especificar que el tenant obtuvo las bases legales necesarias para almacenar datos de sus clientes en un sistema de terceros.
Retención de datos contables: mínimo 5 años según legislación tributaria CR. BookMind no puede borrar datos contables aunque el cliente solicite eliminación — se documenta en ToS.
7. Log de auditoría contable
Toda acción de usuario sobre datos contables queda registrada:
-- Usando la tabla sm_audit_log compartida del stack Cheryx
INSERT INTO sm_audit_log (tenant_id, user_id, accion, tabla, registro_id, detalle_json, ip)
VALUES (
:tenant_id, :user_id,
'bk_asiento_creado',
'bk_asientos', :asiento_id,
'{"total_debito": 8500, "descripcion": "Venta FE A-0234", "origen": "auto_fe"}',
:ip_address
);
Eventos auditados específicos de BookMind:
- bk_fe_emitida — toda emisión de FE
- bk_fe_anulada — toda anulación (requiere motivo)
- bk_asiento_creado — todo asiento (manual o automático)
- bk_conciliacion_cerrada — cierre de período de conciliación
- bk_periodo_cerrado — cierre contable de período
- bk_credenciales_accedidas — acceso a credenciales FE (nivel fe_service_role)
- bk_reporte_exportado — exportación de estados financieros
8. Retención de datos
| Tipo de dato | Retención mínima | Base legal |
|---|---|---|
| FE (XML completo) | 5 años | Código de Normas Tributarias CR, Art. 52 |
| Asientos contables | 5 años | Código de Comercio CR, Art. 251 |
| Movimientos bancarios | 5 años | Reglamento DGT |
| Datos personales de usuarios | Hasta solicitud de eliminación (ARCO) | Ley 8968 |
| Datos de clientes del tenant (AR) | 5 años (dato contable) | Código Tributario |
Flujo de eliminación de cuenta: cuando un tenant cancela, BookMind puede eliminar los datos de usuarios (nombre, email) pero retiene los datos contables (FE, asientos) el tiempo legalmente requerido en formato encriptado, no accesible desde el portal.
9. Checklist de seguridad pre-lanzamiento
- [ ]
REVOKE UPDATE/DELETEen tablas inmutables verificado con test de DB - [ ] RLS habilitado en todas las tablas
bk_*+ test cross-tenant (query retorna 0 filas) - [ ] Credenciales FE encriptadas con pgcrypto — verificado que no aparecen en logs ni en API responses
- [ ] Rate limiting en
/api/v1/bookmind/fe/emitir(max 100/min por tenant) - [ ] Sandbox de Hacienda CR verificado antes de lanzamiento (no enviar FE reales en tests)
- [ ] Retención de XML completo (enviado + respuesta) verificada
- [ ] Audit log funcionando para los 8 eventos principales
- [ ] Penetration test básico de cross-tenant antes de lanzamiento
- [ ] DPA actualizado con los datos procesados de AR (clientes del tenant)
- [ ] Backups encriptados de
bk_integraciones(Backblaze B2 con server-side encryption)
Ver también: Doc 08 (Modelo de Datos — REVOKE UPDATE en bk_asientos) · Doc 07 (SRD Backend — cola FE y credenciales) · Doc 14 (Marco Legal — ToS, DPA y PRODHAB)