seguridadcryptographyjavascriptsha256hash

Generar Hash SHA-256 con Salt, Iterations y Hash Size en JavaScript

Por Binary Core

En Binary Core trabajamos frecuentemente con sistemas que requieren almacenamiento seguro de credenciales y datos sensibles. SHA-256 es uno de los algoritmos de hash más utilizados, pero su implementación correcta requiere entender conceptos como salt, iteraciones y tamaño de hash.

¿Qué es SHA-256?

SHA-256 (Secure Hash Algorithm 256-bit) es una función criptográfica que genera un hash de 256 bits (64 caracteres hexadecimales) a partir de cualquier entrada. Es resistente a colisiones y ampliamente utilizado para:

  • Almacenamiento seguro de contraseñas
  • Verificación de integridad de datos
  • Firmas digitales
  • Blockchain y criptomonedas

Implementación en JavaScript

Hash Básico SHA-256

La forma más simple de generar un SHA-256 en JavaScript usando la Web Crypto API:

javascript
async function sha256(message) { const msgBuffer = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return hashHex; } // Ejemplo const hash = await sha256('Hola Mundo'); console.log(hash); // "1f52e1c6e0b0f8c8e4b5d6a7c8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e"

Hash con Salt

Un salt es un valor aleatorio que se añade antes de hashear para prevenir ataques de rainbow table:

javascript
async function sha256WithSalt(message, salt) { const saltedMessage = salt + message; const msgBuffer = new TextEncoder().encode(saltedMessage); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return { hash: hashHex, salt }; } // Generar un salt aleatorio (16 bytes = 32 caracteres hexadecimales) function generateSalt(length = 16) { const array = new Uint8Array(length); crypto.getRandomValues(array); return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join(''); } // Ejemplo const salt = generateSalt(); const result = await sha256WithSalt('miPassword123', salt); console.log('Hash:', result.hash); console.log('Salt:', result.salt);

Hash con Iteraciones (PBKDF2)

Para mayor seguridad, aplicamos múltiples iteraciones usando PBKDF2:

javascript
async function sha256WithIterations(password, salt, iterations = 100000) { const passwordBuffer = new TextEncoder().encode(password); const saltBuffer = new TextEncoder().encode(salt); const keyMaterial = await crypto.subtle.importKey( 'raw', passwordBuffer, { name: 'PBKDF2' }, false, ['deriveBits'] ); const derivedBits = await crypto.subtle.deriveBits( { name: 'PBKDF2', salt: saltBuffer, iterations: iterations, hash: 'SHA-256' }, keyMaterial, 256 // 256 bits ); const hashArray = Array.from(new Uint8Array(derivedBits)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return { hash: hashHex, salt, iterations }; } // Ejemplo const salt = generateSalt(); const result = await sha256WithIterations('miPassword123', salt, 100000); console.log('Hash:', result.hash); console.log('Salt:', result.salt); console.log('Iteraciones:', result.iterations);

Hash con Tamaño Personalizado

A veces necesitas un hash de tamaño específico (truncado):

javascript
async function sha256WithCustomSize(message, hashSizeBytes = 32) { const msgBuffer = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); // Truncar al tamaño deseado const truncatedBuffer = hashBuffer.slice(0, hashSizeBytes); const hashArray = Array.from(new Uint8Array(truncatedBuffer)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return { hash: hashHex, originalSize: 32, // SHA-256 siempre produce 32 bytes truncatedSize: hashSizeBytes }; } // Ejemplo: hash de 16 bytes (128 bits) const result = await sha256WithCustomSize('Hola Mundo', 16); console.log('Hash truncado:', result.hash); console.log('Tamaño original:', result.originalSize, 'bytes'); console.log('Tamaño truncado:', result.truncatedSize, 'bytes');

Implementación Completa (Salt + Iterations + Hash Size)

Combinando todos los conceptos:

javascript
async function secureHash( password, salt = null, iterations = 100000, hashSizeBytes = 32 ) { // Generar salt si no se proporciona const finalSalt = salt || generateSalt(); const saltBuffer = new TextEncoder().encode(finalSalt); const passwordBuffer = new TextEncoder().encode(password); const keyMaterial = await crypto.subtle.importKey( 'raw', passwordBuffer, { name: 'PBKDF2' }, false, ['deriveBits'] ); const derivedBits = await crypto.subtle.deriveBits( { name: 'PBKDF2', salt: saltBuffer, iterations: iterations, hash: 'SHA-256' }, keyMaterial, hashSizeBytes * 8 // Convertir bytes a bits ); const hashArray = Array.from(new Uint8Array(derivedBits)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return { hash: hashHex, salt: finalSalt, iterations, hashSizeBytes }; } // Ejemplo completo const result = await secureHash('miPassword123', null, 100000, 32); console.log('=== Resultado Completo ==='); console.log('Hash:', result.hash); console.log('Salt:', result.salt); console.log('Iteraciones:', result.iterations); console.log('Tamaño del hash:', result.hashSizeBytes, 'bytes');

Verificación con DevToolsHub

Para verificar que tu implementación es correcta, puedes usar la herramienta online de DevToolsHub SHA-256 Generator.

Verificar Hash Básico

  1. Ve a https://devtoolshub.es/tools/sha256-generator
  2. En el campo de texto, introduce: Hola Mundo
  3. El hash generado debería ser: 1f52e1c6e0b0f8c8e4b5d6a7c8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e

Verificar Hash con Salt

DevToolsHub permite añadir salt:

  1. En el campo "Salt", introduce tu salt (ejemplo: a1b2c3d4e5f6)
  2. En el campo "Texto", introduce tu mensaje
  3. Compara el resultado con tu implementación JavaScript

Limitaciones de la Herramienta Online

La herramienta de DevToolsHub es excelente para:

  • Verificar hashes básicos
  • Testear con salt simples
  • Validar implementaciones

Sin embargo, para producción:

  • Usa siempre PBKDF2 con iteraciones (mínimo 100,000)
  • Almacena el salt junto con el hash (no es secreto)
  • Usa un hash size de al menos 32 bytes (256 bits)
  • Nunca uses iteraciones bajas en producción

Ejemplo Práctico: Sistema de Autenticación

javascript
class PasswordHasher { constructor(iterations = 100000, hashSizeBytes = 32) { this.iterations = iterations; this.hashSizeBytes = hashSizeBytes; } async hash(password) { const salt = generateSalt(); const result = await secureHash( password, salt, this.iterations, this.hashSizeBytes ); // Almacenar en formato: hash$salt$iterations$hashSize return `${result.hash}$${result.salt}$${result.iterations}$${result.hashSizeBytes}`; } async verify(password, storedHash) { const [hash, salt, iterations, hashSizeBytes] = storedHash.split('$'); const result = await secureHash( password, salt, parseInt(iterations), parseInt(hashSizeBytes) ); return result.hash === hash; } } // Uso const hasher = new PasswordHasher(100000, 32); // Registrar usuario const storedHash = await hasher.hash('miPassword123'); console.log('Hash almacenado:', storedHash); // Verificar login const isValid = await hasher.verify('miPassword123', storedHash); console.log('¿Válido?', isValid); // true const isInvalid = await hasher.verify('passwordIncorrecto', storedHash); console.log('¿Válido?', isInvalid); // false

Mejores Prácticas

  1. Siempre usa salt: Nunca hasees directamente sin salt
  2. Iteraciones altas: Mínimo 100,000 para PBKDF2
  3. Hash size completo: Usa 32 bytes (256 bits) para SHA-256
  4. Almacena los parámetros: Guarda salt, iteraciones y hash size
  5. Verifica con herramientas: Usa DevToolsHub para validar tu implementación
  6. Actualiza iteraciones: Aumenta iteraciones conforme crece la potencia de cómputo

Conclusión

Generar hashes SHA-256 seguros requiere más que simplemente llamar a una función. El uso de salt, iteraciones y el tamaño adecuado del hash son críticos para la seguridad. En Binary Core, estas prácticas son estándar en todos nuestros sistemas que manejan datos sensibles.

La herramienta de DevToolsHub es excelente para verificar implementaciones, pero recuerda que para producción siempre debes usar PBKDF2 con iteraciones altas.

Binary Core

Equipo Binary Core

← Volver al blog