Algoritmos criptográficos
Passwork utiliza diferentes algoritmos criptográficos en el servidor y en el cliente.
Resumen de algoritmos
| Propósito | Algoritmo | Dónde se utiliza |
|---|---|---|
| Derivación de clave a partir de contraseña | PBKDF2 | Generación de clave maestra |
| Cifrado simétrico (servidor) | AES-256-CFB | Cifrado de base de datos del lado del servidor |
| Cifrado simétrico (cliente) | CryptoJS AES-256-CBC | Todos los datos del cliente |
| Cifrado asimétrico | RSA-OAEP | Intercambio de claves |
| Hashing | SHA-256, SHA-512 | Verificación de contraseña, integridad |
| Generación de números aleatorios | WebCrypto API | Generación de claves, IV, salt |
PBKDF2 (Password-Based Key Derivation Function 2)
PBKDF2 se utiliza para derivar una clave criptográfica a partir de la contraseña maestra del usuario.
Parámetros del cliente
| Parámetro | Valor |
|---|---|
| Algoritmo | PBKDF2 |
| Función hash | SHA-256 |
| Iteraciones | 300.000 |
| Longitud de clave de salida | 512 bits |
| Salt | 20 caracteres, único por usuario |
| Biblioteca | pbkdf2 (npm) |
Parámetros del servidor
| Parámetro | Valor |
|---|---|
| Algoritmo | PBKDF2 |
| Función hash | SHA-512 |
| Iteraciones | 600.000 |
| Longitud de clave de salida | 512 bits |
| Biblioteca | PHP hash_pbkdf2() |
SHA-256 con 300.000 iteraciones se utiliza en el cliente para lograr un equilibrio entre seguridad y rendimiento del navegador. Se aplican parámetros más estrictos (SHA-512, 600.000 iteraciones) en el servidor para el hashing de contraseñas de autenticación.
Salt
| Parámetro | Valor |
|---|---|
| Longitud | 20 caracteres |
| Alfabeto | A-Z, a-z, 0-9, @, ! (64 caracteres) |
| Entropía | ~120 bits |
| Generación | Servidor, criptográficamente segura |
| Almacenamiento | Servidor, en el perfil del usuario |
| Unicidad | Único por usuario |
El salt siempre se genera en el servidor y se transmite al cliente. El cliente no genera el salt de forma independiente, lo que garantiza el uso de un generador criptográficamente seguro.
Formato del resultado
El resultado de PBKDF2 en el cliente se codifica como una cadena:
pbkdf:sha256:300000:64:{salt}
Propósito del alto número de iteraciones
Un alto número de iteraciones (300.000+) ralentiza significativamente los ataques de fuerza bruta:
- Cada intento requiere un tiempo considerable
- Se dificulta la fuerza bruta paralela con GPU
- Las tablas precalculadas son imposibles debido al salt único
Migración automática de hashes
El sistema admite la migración automática de hashes PBKDF2 cuando cambian los parámetros.
Formato del hash del servidor:
pbkdf:{base64_hash}:{algorithm}:{iterations}:{length}:{salt}
Ejemplo: pbkdf:Abc123...==:sha512:600000:64:xY3zKmN9qR2w...
Mecanismo de migración:
- El usuario inicia sesión en el sistema
- El sistema verifica los parámetros del hash existente
- Si los parámetros difieren de la configuración actual → se requiere rehashing
- La contraseña se hashea automáticamente con los nuevos parámetros
- El nuevo hash se guarda en la base de datos
Lo que se puede actualizar:
| Parámetro | Valor actual | Ejemplo de nuevos valores |
|---|---|---|
| Algoritmo de hash | SHA-512 | SHA3-512, BLAKE2b512 |
| Número de iteraciones | 600.000 | 1.000.000, 2.000.000 |
| Longitud de clave | 512 bits | 256, 1024 bits |
Cuando se actualizan los parámetros:
- Nuevos usuarios → obtienen inmediatamente hashes con los nuevos parámetros
- Usuarios existentes → la migración ocurre en el siguiente inicio de sesión
- Usuarios sin inicio de sesión → los hashes antiguos siguen funcionando
La migración es transparente para los usuarios — simplemente introducen su contraseña como siempre.
AES (Advanced Encryption Standard)
Passwork utiliza diferentes modos AES en el servidor y en el cliente.
Cifrado del servidor: AES-256-CFB
AES-256 en modo CFB (Cipher Feedback) mediante OpenSSL se utiliza en el servidor.
| Parámetro | Valor |
|---|---|
| Algoritmo | AES-256-CFB |
| Longitud de clave | 256 bits |
| Tamaño de bloque | 128 bits |
| Vector de inicialización (IV) | 128 bits, aleatorio por cifrado |
| Biblioteca | OpenSSL (vía PHP) |
Características del modo CFB:
- Modo de cifrado de flujo
- No requiere relleno
- El IV se almacena con el texto cifrado
Uso: cifrado de datos en la base de datos (enlaces externos, configuración, contraseñas LDAP/SMTP).
Generación de IV del servidor:
| Parámetro | Valor |
|---|---|
| Longitud | 128 bits |
| Generador | Criptográficamente seguro (CSPRNG del SO) |
| Unicidad | Nuevo IV para cada operación de cifrado |
| Almacenamiento | Al inicio de los datos cifrados |
Estructura de datos cifrados: [128 bit IV][ciphertext]
Cifrado del cliente: CryptoJS AES-256-CBC
La biblioteca CryptoJS se utiliza en el cliente para todas las operaciones de cifrado simétrico.
| Parámetro | Valor |
|---|---|
| Biblioteca | CryptoJS |
| Modo | CBC (Cipher Block Chaining) |
| Longitud de clave | 256 bits |
| Relleno | PKCS#7 |
| IV | Automático (128 bits) |
| KDF | Función de derivación de clave integrada |
| Codificación de salida | Base32 |
Transformación de clave a AES-256:
Las claves simétricas en Passwork tienen 100 caracteres (~596 bits de entropía), pero AES-256 requiere exactamente una clave de 256 bits. CryptoJS realiza automáticamente la transformación mediante una función de derivación de clave (KDF):
- Se genera un salt aleatorio de 64 bits
- La clave original de 100 caracteres se transforma en una clave AES de 256 bits
- El salt se guarda con el texto cifrado para el descifrado
Los 596 bits de entrada garantizan la máxima fortaleza de la clave de salida de 256 bits. Esto también asegura la unificación — todas las claves simétricas (bóveda, registro, archivo adjunto) se generan de forma idéntica.
Generación de IV del cliente:
CryptoJS genera automáticamente el IV para cada cifrado:
| Parámetro | Valor |
|---|---|
| Longitud | 128 bits |
| Generador | window.crypto.getRandomValues() |
| Unicidad | Nuevo IV para cada operación de cifrado |
| Formato de almacenamiento | Incluido en formato compatible con OpenSSL |
Formato de salida de CryptoJS: "Salted__" + [64 bit salt] + [ciphertext with IV] → Base64
¿Por qué diferentes modos?
| Criterio | Servidor (CFB) | Cliente (CBC) |
|---|---|---|
| Biblioteca | OpenSSL | CryptoJS |
| Razón de elección | Estándar de PHP | Compatibilidad retroactiva |
| Ventajas | Modo de flujo | Amplio soporte |
CryptoJS vs WebCrypto API
La biblioteca CryptoJS se utiliza para el cifrado simétrico en el cliente, no la API nativa WebCrypto. A continuación se presenta una comparación.
Comparación:
| Aspecto | CryptoJS | WebCrypto API |
|---|---|---|
| Implementación | JavaScript puro | Código nativo del navegador |
| Rendimiento | Más lento | Significativamente más rápido |
| Protección contra ataques de temporización | Sin garantías | Protección a nivel de implementación |
| Gestión de memoria | Claves en el heap de JS | Las claves pueden ser no extraíbles |
| API | Síncrona | Asíncrona (Promise) |
| Compatibilidad | Cualquier entorno JS | Solo navegadores modernos |
Por qué se utiliza CryptoJS:
- Compatibilidad retroactiva — los datos cifrados existentes utilizan el formato CryptoJS
- API síncrona — integración más sencilla con la base de código existente
- Universalidad — funciona en cualquier entorno JavaScript
Medidas de seguridad en la implementación actual:
- PBKDF2 mediante biblioteca separada — la clave maestra se deriva mediante PBKDF2 criptográficamente robusto
- WebCrypto para RSA — las operaciones asimétricas críticas se realizan mediante la API nativa
- Generación de IV criptográficamente segura — mediante
crypto.getRandomValues(), noMath.random() - Los datos se cifran una vez — los ataques de temporización requieren múltiples operaciones con la misma clave
Evaluación de riesgos:
| Amenaza | Nivel de riesgo | Justificación |
|---|---|---|
| Ataques de temporización | Bajo | Requiere acceso local al navegador |
| Exposición de memoria | Bajo | Con dicho acceso, es más fácil interceptar la contraseña durante la entrada |
| CVE conocidos | Mínimo | La versión de CryptoJS utilizada no tiene vulnerabilidades críticas |
Las versiones futuras planifican una transición gradual a la API WebCrypto para el cifrado simétrico. Esto permitirá:
- Usar AES-GCM (cifrado autenticado) en lugar de AES-CBC
- Mejora del rendimiento de 10-100x
- Protección de claves a nivel del navegador
La migración se realizará con compatibilidad retroactiva para los datos existentes.
RSA (Rivest–Shamir–Adleman)
RSA se utiliza para el cifrado asimétrico — intercambio seguro de claves simétricas entre usuarios.
Parámetros
| Parámetro | Valor |
|---|---|
| Algoritmo | RSA-OAEP |
| Longitud del módulo | 2048 bits |
| Exponente público | 65537 (0x010001) |
| Función hash | SHA-256 |
| Biblioteca | WebCrypto API (crypto.subtle) |
Generación de claves
Las claves RSA se generan en el cliente mediante la API WebCrypto:
crypto.subtle.generateKey({
name: 'RSA-OAEP',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: 'SHA-256'
}, true, ['encrypt', 'decrypt'])
Formatos de almacenamiento
| Clave | Formato | Descripción |
|---|---|---|
| Pública | SPKI (PEM) | SubjectPublicKeyInfo |
| Privada | PKCS#8 (PEM) | Cifrada con clave maestra |
Formatos compatibles
El sistema admite la importación de claves en formatos:
- PKCS#1 —
-----BEGIN RSA PRIVATE KEY----- - PKCS#8 —
-----BEGIN PRIVATE KEY----- - SPKI —
-----BEGIN PUBLIC KEY----- - JWK — JSON Web Key (para conversión)
Compatibilidad retroactiva
Para claves heredadas de 1024 bits, se utiliza la biblioteca JSEncrypt como alternativa, ya que la API WebCrypto no admite RSA-1024.
¿Por qué RSA-2048?
| Aspecto | Descripción |
|---|---|
| Seguridad | Equivalente a 112 bits de cifrado simétrico |
| Rendimiento | Velocidad aceptable en el navegador |
| Compatibilidad | Amplio soporte en todos los navegadores |
Hashing
SHA-256
| Parámetro | Valor |
|---|---|
| Longitud del hash | 256 bits |
| Biblioteca (cliente) | js-sha256 |
| Uso | Hash de clave maestra, PBKDF2, RSA-OAEP |
SHA-512
| Parámetro | Valor |
|---|---|
| Longitud del hash | 512 bits |
| Biblioteca (cliente) | js-sha512 |
| Biblioteca (servidor) | PHP hash() |
| Uso | PBKDF2 del servidor, hash de clave del servidor, índices de búsqueda |
Generación de números aleatorios
Todos los parámetros criptográficos se generan utilizando generadores de números aleatorios criptográficamente seguros (CSPRNG).
Fuentes de entropía
Servidor (PHP)
| Generador | Fuente de entropía | Uso |
|---|---|---|
random_bytes() | CSPRNG del SO | Datos binarios (claves, tokens) |
random_int() | CSPRNG del SO | Cadenas de caracteres (salts, códigos) |
Fuentes de entropía del SO:
- Linux:
/dev/urandom, llamada al sistemagetrandom() - Windows:
CryptGenRandom()(CryptoAPI) - macOS:
arc4random_buf()
Cliente (JavaScript)
| Generador | Fuente de entropía | Uso |
|---|---|---|
window.crypto.getRandomValues() | CSPRNG del navegador/SO | Números, arrays |
CryptoJS.lib.WordArray.random() | crypto.getRandomValues() | IV para AES |
Fuentes de entropía del navegador:
- Chromium/Electron: BoringSSL CSPRNG
- Firefox: NSS (Network Security Services)
- Safari: CommonCrypto (Apple)
Garantías de fortaleza criptográfica
| Propiedad | Garantía |
|---|---|
| Imprevisibilidad | Imposible predecir el siguiente valor |
| Distribución uniforme | Todos los valores son igualmente probables |
| Sin sesgo | Se utiliza muestreo por rechazo |
| Disponibilidad | Los generadores no se bloquean |
Claves y tokens generados
| Qué se genera | Longitud | Alfabeto | Entropía |
|---|---|---|---|
| Claves simétricas | |||
| Clave de bóveda | 100 caracteres | A-Z, a-z, 0-9, @, ! | ~596 bits |
| Clave de registro | 100 caracteres | A-Z, a-z, 0-9, @, ! | ~596 bits |
| Clave de archivo adjunto | 100 caracteres | A-Z, a-z, 0-9, @, ! | ~596 bits |
| Clave de enlace | 100 caracteres → 256 bits | A-Z, a-z, 0-9, @, ! | ~596 bits entrada |
| Clave de cifrado del servidor | 256 bits | Binario | 256 bits |
| Salts | |||
| Salt PBKDF2 | 20 caracteres | A-Z, a-z, 0-9, @, ! | ~120 bits |
| Salt de bóveda | 32 caracteres | A-Z, a-z, 0-9, @, ! | ~190 bits |
| Tokens y secretos | |||
| Secret Code (localStorage) | 100 caracteres | A-Z, a-z, 0-9 | ~596 bits |
| Extension Secret | 60 caracteres | A-Z, a-z, 0-9 | ~357 bits |
| Auth Token (extensión) | 400 bits | Hex | 400 bits |
| Token de enlace externo | 43 caracteres | A-Z, a-z, 0-9 | ~256 bits |
| Tokens de sesión | |||
| Access Token | 256 bits | Base64 | 256 bits |
| Refresh Token | 256 bits | Base64 | 256 bits |
| Vectores de inicialización | |||
| IV (servidor, AES-CFB) | 128 bits | Binario | 128 bits |
| IV (cliente, AES-CBC) | 128 bits | Binario | 128 bits |
Verificación de unicidad de tokens
Para los tokens que requieren unicidad garantizada (tokens de enlace, códigos de invitación), se realiza una verificación en la base de datos:
- Se genera un token aleatorio
- Se verifica la unicidad en la base de datos
- En caso de colisión — se regenera
- Se devuelve un token garantizado como único
Probabilidad de colisión:
- 43 caracteres de 62: ~10^77 combinaciones
- Con mil millones de tokens: probabilidad de colisión ≈ 10^-59
- Prácticamente imposible
Bibliotecas criptográficas
Cliente (JavaScript/TypeScript)
| Biblioteca | Propósito |
|---|---|
| WebCrypto API | Operaciones RSA, números aleatorios |
| CryptoJS | Cifrado AES (todos los datos) |
| js-sha256 | Hashing SHA-256 |
| js-sha512 | Hashing SHA-512 |
| pbkdf2 | Derivación de clave PBKDF2 |
| node-jsencrypt | Alternativa RSA (1024 bits) |
| pem-jwk | Conversión PEM ↔ JWK |
| totp-generator | Generación de códigos TOTP |
Servidor (PHP)
| Componente | Propósito |
|---|---|
| OpenSSL | Cifrado AES-256-CFB |
| hash() | Hashing SHA-256, SHA-512 |
| hash_pbkdf2() | Derivación de clave PBKDF2 |
| random_bytes() | Generación de datos aleatorios |
Comparación con estándares de la industria
| Parámetro | Passwork | Recomendaciones NIST (2024) | Estado |
|---|---|---|---|
| Cifrado simétrico | AES-256 | AES-128/192/256 | ✓ |
| Cifrado asimétrico | RSA-2048 | RSA-2048+ | ✓ |
| Hashing | SHA-256/512 | Familia SHA-2 | ✓ |
| Iteraciones PBKDF2 | 300K/600K | ≥310K (SHA-256) | ✓ |
| Generador de números aleatorios | CSPRNG | CSPRNG | ✓ |