PassFortressPassFortressPassFortress
SeguridadWhitepaperTérminosPrivacidadCookiesAviso legal

Documento técnico · ← Volver a Seguridad

Whitepaper de seguridad de PassFortress

Versión 1.0 · Última revisión: 9 de julio de 2026 · Idioma: español (España)

Este documento describe el diseño de seguridad de PassFortress con el nivel de detalle que un revisor técnico necesita para evaluarlo: los algoritmos y parámetros exactos, el modelo de amenazas y, lo que más importa, dónde están los límites del modelo. La credibilidad de un gestor de contraseñas se mide en especificidad, no en adjetivos; por eso aquí no hay ninguno que no puedas contrastar. Para la versión en lenguaje llano, consulta la página de Seguridad.


Índice

  1. 1. Resumen ejecutivo
  2. 2. Alcance y audiencia
  3. 3. Arquitectura del sistema
  4. 4. Modelo de datos: qué ve el servidor
  5. 5. Diseño criptográfico
  6. 6. La master key: ciclo de vida
  7. 7. Espacios: cifrado de sobre
  8. 8. Autenticación y sesión
  9. 9. Defensas anti-abuso
  10. 10. API pública programática
  11. 11. Autenticador TOTP
  12. 12. Modelo de amenazas
  13. 13. Límites del modelo
  14. 14. Decisiones de ingeniería
  15. 15. Divulgación responsable
  16. 16. Glosario y referencias

1. Resumen ejecutivo

PassFortress es una bóveda de secretos con un modelo de conocimiento cero del lado servidor. Cada usuario desbloquea su bóveda con una master key que nunca se persiste: no se escribe en la base de datos, ni en disco, ni en registros, ni en copias de seguridad. Viaja cifrada por TLS en la cabecera de las peticiones que lo necesitan, se usa unos milisegundos en memoria para descifrar y se descarta.

Las propiedades centrales del diseño son:

  • Defensa en profundidad. El valor de cada secreto atraviesa tres capas de cifrado independientes antes de tocar la base de datos.
  • La capa exterior de datos depende de una llave que el servidor no guarda. El núcleo se cifra con AES-256-OCB bajo una clave derivada de la master key del usuario (o de la llave del Espacio, en bóvedas compartidas). Un volcado completo de la base de datos es indescifrable sin esa llave.
  • Compartir sin ceder la master key. Los Espacios usan cifrado de sobre con un par de claves Curve25519 por usuario; invitar a alguien no requiere su master key ni expone la de nadie, y expulsarlo rota la llave del Espacio (revocación real).
  • Honestidad sobre el alcance. El cifrado es del lado servidor, no de dispositivo a dispositivo; los metadatos (nombres, notas, sitios, fechas) son visibles para el servidor. Ambas cosas se detallan en las secciones 4 y 13.

2. Alcance y audiencia

Este whitepaper cubre el diseño criptográfico y de autenticación del producto en su estado actual. Está dirigido a personal de seguridad, equipos de compras que realizan due diligence, y usuarios técnicos que quieren verificar las afirmaciones de la página de Seguridad. Sigue el principio de Kerckhoffs: la seguridad del sistema no depende de ocultar su diseño, sino de la clave. Por eso publicamos los algoritmos y parámetros completos.

Estado de validación externa: a fecha de esta revisión, la criptografía de PassFortress no ha sido auditada por un tercero independiente. Encargar una auditoría externa y publicar el informe completo está en nuestra hoja de ruta. Hasta entonces, este documento es una descripción verificable del diseño, no un certificado.

3. Arquitectura del sistema

PassFortress es un monorepo con dos componentes desplegables:

  • Backend: Django 5.2 + Django REST Framework, autenticación JWT (SimpleJWT), base de datos MySQL 8. Toda la lógica criptográfica vive en el servidor, en el paquete core/crypto/. La API se expone bajo /api/v1/.
  • Frontend: Next.js (App Router) + React. La master key vive solo en memoria del navegador y en sessionStorage (nunca en localStorage ni en el servidor). Los tokens de sesión JWT sí viven en localStorage para dar persistencia de sesión.

Alojamiento: la bóveda y la cuenta se alojan en servidores de la Unión Europea (Alemania). El tratamiento se rige por el RGPD y la LOPDGDD; los proveedores actúan como encargados del tratamiento en el sentido del art. 28 RGPD.

Endurecimiento del transporte y del navegador: política de seguridad de contenido (CSP) restrictiva sin scripts de terceros en las rutas donde se teclea la master key; frame-ancestors 'none' + X-Frame-Options: DENY (la aplicación nunca puede embeberse en un iframe, protección anti-clickjacking crítica para un gestor de contraseñas); HSTS en producción; y una identidad visual de la bóveda que no solicita favicons a terceros, para no filtrar a un externo qué servicios usa el usuario.

4. Modelo de datos: qué ve el servidor

Esta es la sección más importante para entender el alcance real del conocimiento cero. Solo el valor del secreto se cifra bajo tu master key. El resto de campos son metadatos que el servidor ve en claro, porque son los que permiten mostrarte y buscar en tus listados sin pedirte la llave en cada pantalla.

Cifrado (tres capas, ilegible sin la llave del usuario/Espacio):

  • El valor de cada secreto y de cada versión de su historial (contraseñas, contenido de archivos .env, documentos, y la URI otpauth:// de los códigos 2FA).
  • La clave privada del par de claves de Espacios de cada usuario.
  • El canary de validación de la master key y la clave Fernet de servidor.

En claro (metadatos visibles para el servidor):

DatoEjemplos
Nombres y organizaciónNombre del secreto, nombre de fichero, nombres y descripciones de carpetas
Sitios web asociadosHostname y URL de login de cada sitio
Notas y campos personalizados (identifiers)El texto libre de las notas y los pares clave-valor. Importante: el valor de un identifier se guarda en claro pese a su nombre. Guarda cualquier cosa que deba ser secreta en el valor del secreto, nunca en las notas ni en un identifier.
Marcas de tiempoFechas de creación de cada versión del valor, fecha de importación
Cuenta e identidadEmail, datos de perfil, tipo de cada secreto
RedDirección IP de conexión; rangos de tus listas blancas/negras de IP (texto en claro)

5. Diseño criptográfico

El valor de un secreto se protege con tres capas encadenadas. De fuera hacia dentro tal como se guarda en la columna de la base de datos: Capa 1 (cifrado de campo) envuelve a Capa 2 (Fernet de servidor), que envuelve a Capa 3 (AES-256-OCB bajo la llave del usuario). En composición de funciones: guardado = capa1( capa2( capa3( valor ) ) ). Solo la Capa 3 usa una llave que el servidor no almacena.

5.1 Capa 1: Cifrado de campo (clave de servidor)

Se aplica a nivel de columna de base de datos. Es una réplica byte-a-byte de django-cryptography 1.1 (que no es instalable en Django 5.2 porque importa un módulo eliminado en Django 5.0), reimplementada en core/crypto/.

ParámetroValor
Derivación de clavePBKDF2-HMAC-SHA256, 30 000 iteraciones, salida de 32 bytes (AES-256)
Secreto y saltDeriva del SECRET_KEY de Django; salt = CRYPTOGRAPHY_SALT
CifradoAES-256-CBC con relleno PKCS7 (bloque de 128 bits); IV aleatorio de 16 bytes
Serializaciónpickle del valor antes de cifrar
AutenticaciónHMAC-SHA256 con el SECRET_KEY crudo (no la clave derivada), en comparación de tiempo constante
Formato del blobversión(1) · timestamp(8, big-endian) · IV(16) · ciphertext · HMAC(32); columna física longblob

El timestamp forma parte del formato heredado (FernetSigner) pero no se verifica expiración (sin TTL): un valor cifrado sigue siendo válido indefinidamente.

5.2 Capa 2: Fernet de servidor (clave rotable)

Envuelve el valor con Fernet estándar (especificación que combina AES-128-CBC + HMAC-SHA256 con cifrado autenticado). La clave se guarda en la propia base de datos cifrada a su vez por la Capa 1, de modo que en reposo nunca aparece en claro. Su función es la higiene criptográfica: podemos rotarla sin descifrar ni re-cifrar tu bóveda ni pedirte nada. El sistema admite varias claves de servidor solapadas: al descifrar, prueba de la más nueva a la más antigua hasta que una valida, así una rotación no rompe los valores existentes. Rotar la clave Fernet no toca la Capa 3.

5.3 Capa 3: AES-256-OCB con tu llave

Es la capa de conocimiento cero: la única cuya clave el servidor no almacena. Se cifra el núcleo antes de las otras dos capas.

ParámetroValor
Derivación de claveSHA-256 de la master key (bóveda Personal) o de la llave del Espacio en hexadecimal (bóveda compartida) → 32 bytes (AES-256)
CifradoAES-256 en modo OCB (cifrado autenticado con datos asociados)
Nonce15 bytes aleatorios por operación
Tag de autenticación16 bytes
Formato de salidahex( nonce(15) · tag(16) · ciphertext )
FalloCerrado: clave incorrecta, manipulación o entrada inválida devuelven «vacío», nunca datos parciales

Validación de la master key sin almacenarla: la cuenta guarda un canary (un valor conocido cifrado bajo la master key). Para validar una master key entrante, el servidor intenta descifrar el canary; si el resultado es correcto, la llave es válida. Nunca se compara contra una master key almacenada, porque no existe ninguna almacenada.

6. La master key: ciclo de vida

La master key es la pieza de la que depende todo el modelo. Su ciclo de vida está diseñado para que no exista en ningún sitio del que se pueda robar en reposo:

  1. Origen. El usuario la elige al crear la bóveda. Nunca se transmite a terceros (tampoco a Google en el inicio de sesión con Google, que es un paso aparte).
  2. Transporte. Solo las operaciones que cifran o descifran (leer un valor, crear o editar un secreto) la envían, cifrada por TLS, en la cabecera X-Master-Key de esa petición concreta. Los listados de metadatos no la requieren.
  3. Uso. El servidor la usa en memoria durante la petición para derivar la clave AES-256-OCB (Capa 3) y descartar el resultado. No se escribe en base de datos, disco, registros ni copias de seguridad, ni se incluye en la auditoría.
  4. En el navegador. Vive en memoria y en sessionStorage, que se vacía al cerrar el navegador. Al reabrir, se vuelve a pedir.

Semántica de los errores (deliberada): una petición que requiere master key y no la trae recibe 428 (master_key_required), no 401. El 401 se reserva para la sesión JWT; usar 428 evita que el cliente confunda «falta la master key» con «el token de sesión caducó» y cierre sesión por error. Una master key presente pero inválida recibe 403 (master_key_invalid).

Cambio de master key. Cambiarla re-cifra atómicamente la Capa 3 de todos los secretos Personales del usuario y re-envuelve su clave privada de Espacios; no toca las bóvedas compartidas (su cifrado depende de la llave del Espacio, no de la master key). No existe recuperación: sin puerta trasera, si el usuario la olvida, nadie (tampoco nosotros) puede descifrar sus secretos.

7. Espacios: cifrado de sobre

Los Espacios de Trabajo son bóvedas compartidas. Su reto criptográfico es permitir que varias personas accedan a los mismos secretos sin que nadie ceda su master key. Se resuelve con cifrado de sobre y un par de claves invisible por usuario.

  • Par de claves por usuario. Cada usuario tiene un par de claves Curve25519 (NaCl SealedBox). La clave pública se guarda en claro; la privada se guarda envuelta con la triple capa bajo la master key del usuario. Se genera de forma perezosa e idempotente en el primer desbloqueo.
  • Llave del Espacio. Cada Espacio tiene una workspace_key aleatoria de 32 bytes. La Capa 3 de los secretos del Espacio se cifra con esa llave (en hexadecimal) en lugar de con la master key. Las Capas 1 y 2 no cambian.
  • Reparto de la llave. A cada miembro se le entrega la workspace_key cifrada con su clave pública (un «sobre» que solo su clave privada abre). Invitar a alguien solo requiere su clave pública, no su master key ni la de nadie.
  • Revocación real al expulsar. Expulsar a un miembro activo no solo le retira el acceso: rota la workspace_key, re-cifra atómicamente todos los secretos del Espacio con la nueva llave y vuelve a repartir sobres a los miembros restantes. Salir voluntariamente no rota la llave.

Los secretos Personales tienen la llave del Espacio nula y siguen cifrados únicamente con la master key del usuario, sin re-cifrado.

8. Autenticación y sesión

La autenticación de la cuenta es ortogonal a la master key: iniciar sesión te identifica; la master key descifra tu bóveda. Son dos secretos distintos.

Parámetro de sesiónValor
TokensJWT (SimpleJWT), firmados con HS256 sobre el secreto del servidor
Token de acceso1 hora en producción (12 horas en modo desarrollo)
Token de refresco365 días, con rotación deslizante
RotaciónActivada; el token de refresco no es de un solo uso, para que una respuesta de refresco perdida o una carrera entre pestañas no cierre la sesión
Cierre de sesiónAñade a la lista negra el token de refresco presentado; el token de acceso en curso, al ser sin estado, sigue válido hasta que caduca (como mucho 1 hora)

La sesión larga es deliberada (persistencia tipo webmail): los tokens viven en localStorage y sobreviven al cierre del navegador. La master key, en cambio, nunca se persiste y se re-pide al reabrir.

Segundo factor (2FA): verificación en dos pasos por email o SMS para entrar en la cuenta.

Inicio de sesión con Google. Verifica el ID token de Google en el servidor: firma y expiración contra los certificados de Google, emisor esperado, audiencia contra una allowlist (vacía = falla cerrado en producción), email_verified estricto, y presencia de email y sub. Google solo autentica: la cuenta nueva nace sin master key, que el usuario crea en su primer acceso a la bóveda. La master key nunca llega a Google.

9. Defensas anti-abuso

9.1 Bloqueo anti fuerza bruta de la master key

Como la master key se valida contra un canary, hay que impedir que un atacante con sesión válida la adivine por fuerza bruta. El sistema cuenta los intentos fallidos por usuario y bloquea con espera creciente. Todos los umbrales son configurables por variable de entorno; los valores por defecto son:

ParámetroValor por defecto
Fallos permitidos por ventana10
Duración de la ventana3600 s (1 hora)
Espera base del bloqueo60 s, con backoff exponencial (60 s, 120 s, 240 s…)
Espera máxima del bloqueo3600 s (1 hora)
Fallos que auto-revocan una API key20
Longitud mínima de master key nueva8

Al bloquear, las peticiones que requieren master key devuelven 429 master_key_locked con cabecera Retry-After. El gate del bloqueo actúa antes de tocar la criptografía. Una cabecera ausente (428) no cuenta como intento fallido: solo cuenta un valor presente e incorrecto. Una master key correcta resetea todos los contadores. Los contadores viven en la caché compartida (Redis en producción, para que se compartan entre workers) y contienen solo enteros y marcas de tiempo, nunca la master key. Una API key que acumula demasiados fallos se auto-revoca.

9.2 Política de fortaleza de master key

Toda master key nueva (registro o cambio) debe superar una política de fortaleza; devuelve 400 weak_master_key si falla. Se rechaza: longitud menor de 8; un único carácter repetido; secuencias triviales ascendentes o descendentes (p. ej. 12345678); y una lista de claves comunes. La política se aplica solo al fijar o cambiar la master key: las claves cortas preexistentes (grandfathering) siguen funcionando hasta que su dueño las cambie.

10. API pública programática

Toda la API es accesible con API keys (estilo Stripe/GitHub-PAT), no solo con la sesión web.

  • Formato y almacenamiento. Token pf_live_ / pf_test_ seguido de 32 bytes aleatorios en base62, mostrado una sola vez. En base de datos solo se guardan el prefijo, los 4 últimos caracteres y el hash SHA-256 (sin sal) del token; el token crudo nunca se persiste. No lleva sal porque el token tiene 256 bits de entropía (no es fuerza-bruteable) y el lookup debe ser O(1) indexado.
  • Autenticación. Un gate de prefijo pf_ decide si la petición va por API key o por JWT: si no empieza por pf_, la web sigue intacta. Errores (todos 401): clave inválida, revocada, expirada o IP no permitida.
  • Master key igual que en la web. Los metadatos no la requieren; leer valores exige el scope values:read y la master key por cabecera. Son ortogonales: el scope se comprueba antes de la criptografía (403 insufficient_scope) y la master key después (428/403).
  • Scopes de mínimo privilegio. Permisos granulares por recurso y acción (secrets:read, values:read, containers:write…). Acuñar nuevas claves exige apikeys:write, excluido de los presets, y solo concede un subconjunto de los scopes de la propia clave (sin escalada).
  • Límite de tasa por clave. 1000/h por defecto, 10000/h en el tier alto, o sin límite. Una clave con values:read no puede usar el tier sin límite (400 invalid_rate_tier).
  • Auditoría sin fugas. El tráfico de API key se registra con el identificador público de la clave, el endpoint, el estado y el código de error. Nunca se registran el token, su hash, la master key ni los valores. El tráfico de la sesión web no se audita ahí.

11. Autenticador TOTP

El autenticador TOTP integrado (los códigos 2FA de tus servicios de terceros) reutiliza la maquinaria de las contraseñas, con conocimiento cero de extremo a extremo del servidor:

  • El secreto almacenado es la URI canónica otpauth:// completa, cifrada con la triple capa igual que cualquier otro valor. Es opaca para el servidor.
  • El servidor nunca la parsea ni calcula códigos. El código de 6 dígitos (compatible con el estándar TOTP, RFC 6238) se genera en el cliente, en el navegador, tras descifrar la URI con la master key. No viaja al servidor.

12. Modelo de amenazas

Activos protegidos: los valores de los secretos (contraseñas, archivos .env, documentos, semillas TOTP) y su historial. No protegidos por cifrado bajo la master key: los metadatos de la sección 4.

Para cada adversario, qué mitiga el diseño y qué no:

AdversarioMitigaciónResidual
Robo del volcado de la base de datos (o de una copia de seguridad)Los valores están cifrados en Capa 3 con una llave que no está en el volcado; indescifrables. Las Capas 1 y 2 añaden barreras adicionales.Los metadatos en claro (nombres, sitios, notas, fechas) quedan expuestos.
Atacante de red / intermediario (MITM)TLS en todo el transporte; HSTS en producción; la master key solo viaja cifrada por TLS.Depende de la integridad de TLS y de la cadena de certificados del cliente.
Empleado con acceso de lectura al almacenamiento en reposoNo puede leer valores: la master key no está en reposo en ningún soporte, ni en registros ni en auditoría.Puede ver los metadatos. Un operador con control del código en ejecución podría, en un ataque activo, interceptar una master key en memoria mientras se usa (ver sección 13).
Fuerza bruta de la master key (con sesión válida)Bloqueo con backoff exponencial, auto-revocación de API keys abusivas y política de fortaleza para claves nuevas (sección 9).Las master keys cortas preexistentes (grandfathering) son más débiles hasta que se cambian.
API key robadaScopes de mínimo privilegio, allowlist de IP, expiración, revocación y rotación; leer valores requiere además la master key.Con el scope adecuado, expone los metadatos que ese scope permita mientras la clave no se revoque.
Compromiso del navegador del usuario (malware, extensión, XSS)CSP restrictiva sin scripts de terceros donde se teclea la master key; protección anti-clickjacking; la master key nunca en localStorage.Si el dispositivo del usuario está comprometido mientras la bóveda está desbloqueada, la master key y los valores descifrados están, por definición, a su alcance. Ningún cifrado de servidor protege un cliente comprometido.
Miembro expulsado de un EspacioLa llave del Espacio se rota y todo se re-cifra: pierde el acceso criptográfico, no solo el permiso.Cualquier secreto que copiara mientras era miembro sigue en su poder (cierto de cualquier sistema de compartición).

13. Límites del modelo

Ningún modelo de seguridad lo cubre todo. Estos son los nuestros, sin adornos:

  • El cifrado es del lado servidor, no de dispositivo a dispositivo. La master key viaja (siempre por TLS) y se usa unos milisegundos en la memoria del servidor. La garantía de conocimiento cero cubre todo lo que se persiste (base de datos, discos, registros, copias de seguridad), no un ataque activo sobre el código en ejecución.
  • Los metadatos no se cifran con tu master key. Nombres, notas, sitios, fechas e identifiers son visibles para el servidor (sección 4).
  • No hay recuperación de master key. Es la consecuencia directa de no tener puerta trasera, no un defecto pendiente de arreglar.
  • Confianza en el transporte y el cliente. El modelo asume TLS íntegro y un dispositivo de usuario no comprometido mientras la bóveda está desbloqueada.
  • Auditoría independiente: todavía no. La criptografía aquí descrita no ha sido auditada por un tercero. Publicaremos el informe completo cuando se realice.

14. Decisiones de ingeniería

  • Réplica de django-cryptography 1.1. La Capa 1 reimplementa byte-a-byte esa biblioteca porque su versión original no es instalable en Django 5.2 (importa un módulo eliminado en Django 5.0). Reproducir el formato exacto permite leer los datos cifrados heredados sin migrarlos.
  • Pin de pycryptodomex en 3.16.0. Entre las versiones 3.16 y 3.21 cambió el cálculo interno de un offset en AES-OCB con nonce de 15 bytes; versiones posteriores no descifran los blobs existentes. El pin es una salvaguarda de compatibilidad, no una decisión de seguridad, y está documentado en el repositorio.
  • Modo OCB. AES-OCB proporciona cifrado autenticado en un solo paso; el descifrado verifica el tag y falla cerrado ante cualquier manipulación.

15. Divulgación responsable

Si encuentras una vulnerabilidad, escríbenos a contact@passfortress.com con el asunto «Seguridad». Leemos todos los informes y respondemos.

Si tu investigación es de buena fe y coordinada (sin acceder, alterar ni extraer datos de otros usuarios, sin degradar el servicio y dándonos un plazo razonable para corregir antes de publicar) no la consideraremos una infracción de nuestros Términos ni promoveremos acciones legales por ella. Este compromiso no ampara conductas contrarias a la ley ni alcanza a reclamaciones de terceros.

Los detalles completos (alcance, directrices y qué esperar) están en nuestra política de divulgación responsable, publicada también en formato legible por máquina (RFC 9116) en /.well-known/security.txt.

16. Glosario y referencias

Glosario

  • Conocimiento cero (del lado servidor). El servidor no puede leer los valores persistidos sin una llave que no almacena.
  • Cifrado autenticado (AEAD). Cifra y garantiza integridad a la vez; una manipulación del texto cifrado se detecta al descifrar.
  • Cifrado de sobre. Cifrar una llave de datos con otra llave (aquí, la llave del Espacio con la clave pública de cada miembro).
  • KDF (función de derivación de clave). Deriva una clave criptográfica a partir de un secreto (aquí PBKDF2-SHA256 y SHA-256).
  • Canary. Valor conocido cifrado bajo la master key, usado para validarla sin almacenarla.

Referencias

  • RFC 6238, TOTP: Time-Based One-Time Password Algorithm.
  • RFC 7253, The OCB Authenticated-Encryption Algorithm.
  • RFC 8018, PKCS #5: Password-Based Cryptography (PBKDF2).
  • Especificación Fernet (cifrado simétrico autenticado).
  • NaCl / libsodium, SealedBox sobre Curve25519.
  • Página de Seguridad · Política de Privacidad · Términos y Condiciones

Versiones del documento

VersiónFechaCambios
1.09 de julio de 2026Primera publicación.

Este documento describe el modelo de seguridad con fines informativos y de verificación técnica; en caso de discrepancia prevalecen la Política de Privacidad y los Términos y Condiciones. Puedes guardar esta página como PDF desde el diálogo de impresión de tu navegador.

© 2026 PassFortress. Todos los derechos reservados.

SeguridadTérminosPrivacidadCookiesAviso legal