Seguridad
Modelo de seguridad
ZetiSecret es una herramienta para compartir secretos de un solo uso. El objetivo central es que el servidor nunca vea el secreto en claro, incluso ante un compromiso parcial de la infraestructura.
Principios de diseño
- El servidor no recibe secretos en claro ni claves privadas.
- La clave de descifrado viaja únicamente en el fragmento
#…de la URL y nunca llega al servidor. - Los secretos son de un solo uso: se borran de la base de datos en el momento de la lectura.
- Los secretos caducados se eliminan automáticamente.
- Los logs nunca contienen secretos, passphrases, claves privadas ni fragmentos de URL.
- Las funciones administrativas están protegidas por IP y auditadas.
Criptografía
Todo el cifrado ocurre en el navegador del remitente usando WebCrypto API, sin dependencias externas de cifrado.
| Componente | Algoritmo | Notas |
|---|---|---|
| Intercambio de claves | ECDH P-256 | Par efímero por secreto |
| Cifrado del secreto | AES-GCM 256-bit | IV aleatorio de 96-bit |
| Passphrase opcional | PBKDF2-SHA-256 | Salt aleatorio, iteraciones configurables (≥ 310 000) |
| Transporte | TLS 1.2 / 1.3 | HTTPS obligatorio |
¿Por qué ECDH?
El remitente genera un par de claves efímero. La clave pública se guarda en el servidor; la privada va en el
#fragmento del enlace compartido. El servidor nunca puede descifrar el ciphertext porque nunca recibe la clave privada.
Datos guardados en base de datos
La tabla secrets almacena únicamente:
id— identificador opaco del secretociphertext— secreto cifrado con AES-GCMiv— nonce AES-GCM (96-bit aleatorio)public_key— clave pública ECDH del par efímeropassphrase_hash— verificador PBKDF2 (si aplica)passphrase_salt— salt PBKDF2 (si aplica)passphrase_iterations— iteraciones PBKDF2failed_attempts— contador de intentos fallidoslocked_until— bloqueo temporal anti-fuerza-brutacreated_at,expires_at— metadatos de ciclo de vida
Nunca se guarda
El secreto en claro, la clave privada ECDH, la passphrase en claro ni el fragmento
#… del enlace.
Flujo de lectura
-
El destinatario abre la URL
/s/:id#clave_privada_ecdhen su navegador. -
El navegador extrae la clave privada del fragmento
#…. Este fragmento no se envía al servidor. -
Si el secreto tiene passphrase, el navegador solicita al usuario que la introduzca y deriva el verificador localmente con PBKDF2.
-
El backend valida la passphrase (si aplica), comprueba caducidad y estado de bloqueo.
-
Si todo es correcto, borra la fila de la base de datos dentro de una transacción atómica (un solo uso garantizado).
-
Devuelve
ciphertext,ivypublic_keyal navegador. -
El navegador realiza el intercambio ECDH, deriva la clave AES y descifra localmente. El texto nunca abandona el dispositivo del destinatario en claro.
Controles técnicos activos
Transporte y red
- HTTPS obligatorio — redirección automática de HTTP.
- Backend escuchando solo en
127.0.0.1detrás de proxy inverso (App Service / Nginx). - PostgreSQL accesible únicamente desde la subred de aplicación (VNet privado).
Aplicación
- Rate limiting global (todas las rutas) y específico en endpoints sensibles (creación, lectura de secreto).
- Bloqueo temporal progresivo por intentos fallidos de passphrase (configurable, por defecto 5 intentos / 15 min).
- Limpieza automática de secretos caducados en cada ciclo de mantenimiento.
- Endpoint de mantenimiento protegido por token bearer.
Cache-Control: no-storeen todas las respuestas de la API.- Cabeceras de seguridad via Helmet: CSP estricta sin
unsafe-inline, HSTS, X-Frame-Options, etc.
Operaciones
- Panel HQ (
/hq) restringido por IP — no accesible desde internet público. - Logs JSON estructurados: nunca contienen secretos, passphrases, claves ni URLs completas de secretos.
- Certificado TLS renovado automáticamente con Let's Encrypt (ECC secp384r1).
- Dependencias auditadas periódicamente con
npm audit.
Limitaciones actuales
Transparencia sobre lo que falta
Publicamos esta lista porque la transparencia es parte del modelo de seguridad.
- Sin revisión criptográfica externa (pentest / auditoría formal pendiente).
- Sin SAST/DAST continuo en CI.
- Sin modelo multiusuario con RBAC granular (en roadmap enterprise).
- Sin SSO/SAML/OIDC para empresas (en roadmap).
- Sin política formal escrita de retención y borrado.
- Sin programa de bug bounty público.
Roadmap de seguridad
| Característica | Estado |
|---|---|
| Auditoría criptográfica externa | Planificado |
| SAST en pipeline CI | Planificado |
| RBAC granular para equipos | En desarrollo |
| SSO / OIDC enterprise | En roadmap |
| Webhook de auditoría / SIEM | En roadmap |
| Política formal de retención | Planificado |
Cumplimiento normativo: ENS e ISO 27001
Cada componente técnico de ZetiSecret está diseñado para satisfacer controles concretos del Esquema Nacional de Seguridad (RD 311/2022) y de ISO/IEC 27001:2022. La siguiente tabla resume los controles directamente satisfechos por la arquitectura del producto.
| Componente técnico | Controles ENS | Controles ISO 27001 |
|---|---|---|
| ECDH P-256 + AES-GCM (WebCrypto) | mp.info.3, mp.com.2, mp.com.3 | 8.24, 5.14, 8.12 |
| Clave privada solo en fragment URL (#) | mp.com.2, mp.info.3 | 8.12, 8.11 |
| PBKDF2-SHA-256 (passphrase) | mp.info.3, op.acc.5 | 8.24, 8.5 |
| TLS 1.2+ con HSTS (Caddy / App Service) | mp.com.1, mp.if.7 | 8.20, 8.21 |
| Red interna Docker / VNet privado | mp.com.4, mp.if.7 | 8.22, 8.20 |
| Helmet CSP + rate limiting | mp.s.2, op.acc.5 | 8.26, 8.6, 8.5 |
| Logs JSON + audit_events + SIEM webhook | op.exp.8, op.exp.9, mp.info.1 | 8.15, 8.11, 5.28 |
| Borrado atómico en consumo + TTL | mp.info.6, mp.info.9 | 8.10, 5.14 |
| Docker hardening (no-root, read-only) | op.acc.6, op.exp.6 | 8.18, 8.19, 8.7 |
| npm audit en CI + npm ci | op.exp.4, op.pl.3 | 8.8, 5.21 |
Para la evidencia técnica completa de cada control (por qué y cómo cumple, con referencias al código fuente), consulta la página de cumplimiento ENS · ISO 27001 →
¿Tienes preguntas sobre seguridad o quieres reportar una vulnerabilidad? Escríbenos a soporte@zetit.es — respondemos en menos de 48 h.