🔐Ciberseguridad

SHA-256 explicado visualmente: experimenta con las funciones hash en tiempo real

Felipe••11 min de lectura
Compara hashes SHA-256 en tu navegador, observa el efecto avalancha y aprende qué garantizan —y qué no— los 256 bits de una huella criptográfica.

Cambia la H de Hola por una h. El texto sigue siendo reconocible, pero su hash SHA-256 cambia por completo a simple vista. La diferencia no indica que el algoritmo sea aleatorio: si repites exactamente la misma entrada, obtendrás exactamente el mismo resultado.

En este artículo vamos a explorar esa combinación: resultados reproducibles y cambios difíciles de anticipar. El playground permite comparar dos textos sin salir de la explicación. No es una prueba de seguridad del algoritmo ni un lugar para introducir secretos; utiliza únicamente datos ficticios.

De un mensaje a una huella de tamaño fijo

Una función hash criptográfica transforma una secuencia de bits en una salida de longitud fija, diseñada para satisfacer determinadas propiedades de seguridad. Esa salida se llama digest, resumen o huella. No contiene una copia comprimida que después podamos descomprimir.

SHA-256 pertenece a la familia SHA-2. Su diseño especifica un digest de 256 bits, es decir, 32 bytes, tanto para una entrada vacía como para un texto largo dentro del dominio admitido. No elige esa longitud según el mensaje. NIST describe el algoritmo en FIPS 180-4, sección 6.2.

Determinista significa que los mismos bytes, procesados con el mismo algoritmo, producen el mismo digest. SHA-256 no usa una clave secreta ni genera un número aleatorio en cada cálculo. Esto permite que dos personas calculen una huella por separado y comparen sus resultados.

Pero «los mismos bytes» es más preciso que «el mismo texto». Antes de calcular hay que decidir cómo representar los caracteres.

UTF-8: lo que realmente recibe el algoritmo

En el playground, TextEncoder convierte el texto a UTF-8. Una letra ASCII como a ocupa un byte; é ocupa dos; muchos emojis ocupan cuatro o forman secuencias más largas. El contador muestra bytes, no caracteres. MDN: TextEncoder.

No eliminamos espacios, saltos de línea ni acentos. hola, hola y hola\n son entradas distintas. El campo de texto del navegador representa sus saltos de línea como LF; esto no convierte el playground en una herramienta para verificar los bytes originales de un archivo.

También hay textos que se ven iguales y tienen representaciones Unicode diferentes: é puede ser un carácter precompuesto o una e seguida de un acento combinante. Codificar en UTF-8 no normaliza esas secuencias. Si tu aplicación necesita tratarlas como equivalentes, define explícitamente una política de normalización, como NFC, antes del hash. No la apliques sin pensar: estarías cambiando los datos que identificas. MDN: normalización Unicode.

Por qué ves 64 caracteres hexadecimales

Un digest es binario. Para mostrarlo solemos convertir cada byte en dos dígitos hexadecimales, del 0 al f. Cada dígito representa cuatro bits:

256 bits ÷ 4 bits por dígito = 64 caracteres hexadecimales.

El hexadecimal es una representación del resultado, no una capa adicional de seguridad. Puedes representar los mismos 32 bytes en Base64 y seguir teniendo exactamente el mismo digest.

La entrada vacía también produce una salida válida. Su SHA-256 en hexadecimal es:

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

No confundas una cadena vacía con la cadena null, un byte cero o un salto de línea: son entradas diferentes.

El efecto avalancha, observado de cerca

El efecto avalancha describe la difusión de un cambio pequeño en la entrada sobre muchos bits de la salida. Para una función hash criptográfica bien diseñada, al cambiar un bit de entrada se espera que cada bit de salida cambie con una probabilidad cercana a la mitad. No significa que cada pareja de mensajes deba diferir exactamente en 128 de sus 256 bits. Wolfram: demostración del efecto avalancha.

Los ejemplos iniciales son Hola y hola. Sus representaciones UTF-8 difieren en un solo bit: H es 0x48 y h es 0x68. En la ejecución de este ejemplo, sus digests difieren en 138 bits, aproximadamente el 53,9 %.

Prueba a igualar ambas entradas, vaciar una de ellas o añadir un espacio. La cuadrícula compara los bits de los dos resultados; no visualiza las rondas internas de SHA-256.

El playground necesita JavaScript y Web Crypto en un navegador con HTTPS. El artículo y los ejemplos de código siguen disponibles sin activar la herramienta.

Cada casilla marcada con × indica un bit distinto; las casillas sin marca indican coincidencia. El número también se presenta como texto, para que la comparación no dependa del color.

Observar unos pocos pares sirve para entender el fenómeno, no demuestra resistencia a ataques. Una función defectuosa también podría producir resultados de aspecto caótico. Las propiedades criptográficas requieren análisis mucho más amplio que una visualización.

Si solo necesitas generar una huella para tu trabajo habitual, Text Forge ya dispone de una utilidad SHA-256. Este playground tiene otra finalidad: explorar el comportamiento mientras lees y comparar diferencias bit a bit.

Tres preguntas de seguridad diferentes

Decir que un hash «no se puede revertir» es una abreviatura incompleta. Conviene distinguir los problemas que debe dificultar.

Resistencia a preimagen

Te dan un digest d y buscas un mensaje m tal que SHA256(m) = d. La resistencia a preimagen exige que encontrarlo sea computacionalmente inviable en el escenario criptográfico considerado. No existe una operación de descifrado de SHA-256. NIST: resistencia a preimagen.

Eso no impide probar candidatos. Si sabes que el mensaje es uno de diez códigos posibles, puedes calcular sus diez hashes y comparar. No has invertido el algoritmo; has aprovechado un espacio de entradas muy pequeño.

Resistencia a segunda preimagen

Ahora conoces un mensaje concreto m y quieres encontrar otro distinto m2 con el mismo digest. La condición es m2 ≠ m y SHA256(m2) = SHA256(m).

Es relevante cuando alguien intenta sustituir un archivo ya fijado por otro con la misma huella. La entrada de referencia está determinada de antemano. NIST: resistencia a segunda preimagen.

Resistencia a colisiones

Aquí puedes escoger ambos mensajes: buscas cualquier pareja distinta con el mismo digest. Es un problema diferente y, en general, más fácil que encontrar una preimagen de una huella concreta. NIST: resistencia a colisiones.

Las colisiones existen matemáticamente: hay más mensajes posibles que salidas de 256 bits. La propiedad buscada es que resulte inviable encontrarlas, no que no existan. NIST asigna a SHA-256 una resistencia de 128 bits frente a colisiones y de 256 bits frente a preimagen; son medidas de problemas distintos, no garantías de seguridad de una aplicación completa. NIST: propiedades de funciones hash.

Si dos hashes difieren, los bytes de entrada eran diferentes. Si coinciden, no has obtenido una demostración matemática de que los mensajes sean idénticos ni de quién los creó.

Hashing, cifrado y codificación tienen objetivos distintos

OperaciónObjetivoRecuperación de la entrada
SHA-256Obtener una huella para comparar o usar dentro de un protocoloNo tiene operación inversa de descifrado; se pueden probar candidatos
CifradoProteger confidencialidad bajo una claveSe recupera mediante descifrado con la clave y parámetros correctos
Codificación, como Base64Representar datos en otro formatoSe decodifica; no proporciona secreto por sí sola

Publicar el hash de un dato predecible no equivale a cifrarlo. Para profundizar en las diferencias y ver ejemplos ejecutables de las tres técnicas, consulta hashing, cifrado y codificación.

Calcular SHA-256 con la API nativa

El navegador ya ofrece la operación mediante crypto.subtle.digest(). La API recibe bytes y devuelve una promesa con un ArrayBuffer; requiere un contexto seguro, normalmente HTTPS o localhost. No necesitas implementar las rondas de SHA-256 ni instalar una biblioteca criptográfica. MDN: SubtleCrypto.digest.

Este ejemplo es TypeScript y usa el mismo recorrido básico del playground:

async function sha256(texto: string): Promise<string> {
  const bytes = new TextEncoder().encode(texto);
  const buffer = await crypto.subtle.digest('SHA-256', bytes);
  const digest = new Uint8Array(buffer);

  return Array.from(digest, byte =>
    byte.toString(16).padStart(2, '0')
  ).join('');
}

async function ejecutar(): Promise<void> {
  const primero = await sha256('Hola');
  const segundo = await sha256('hola');

  console.log({ primero, segundo });
  console.log('Misma entrada:', primero === await sha256('Hola'));
}

void ejecutar().catch(() => {
  console.error('No se pudo calcular SHA-256.');
});

Para ejecutarlo fuera del navegador, guárdalo como sha256.ts y utiliza Node.js 22 actualizado o una versión posterior con soporte de TypeScript activado por defecto. Node incorpora ese comportamiento desde 22.18; elimina estas anotaciones al ejecutar, pero no comprueba los tipos. Documentación de Node.js.

node sha256.ts

La última comparación devuelve true. Este fragmento calcula y muestra resultados; el playground añade límites, gestión de errores y controles de interacción. El ejemplo utiliza la Web Crypto API de Node.js, no un endpoint del sitio.

Comparar bits, no caracteres del hexadecimal

Dos dígitos hexadecimales distintos pueden diferir en uno, dos, tres o cuatro bits. Por eso contar caracteres diferentes no mide correctamente la distancia entre digests.

La operación XOR produce un 1 donde dos bits difieren. Este fragmento compara arrays de 32 bytes, ya obtenidos mediante digest():

function contarBitsDistintos(a: Uint8Array, b: Uint8Array): number {
  if (a.length !== 32 || b.length !== 32) {
    throw new Error('Se esperan dos digests SHA-256 de 32 bytes.');
  }

  let diferentes = 0;

  for (let i = 0; i < a.length; i++) {
    const diferencia = a[i] ^ b[i];

    for (let bit = 0; bit < 8; bit++) {
      diferentes += (diferencia >>> bit) & 1;
    }
  }

  return diferentes;
}

El playground calcula después de una pausa de 250 ms, descarta resultados de entradas que ya cambiaste y limita cada texto a 16 KiB en UTF-8. Ese es un límite de esta demostración, no de SHA-256. digest() necesita la entrada completa en memoria: este ejemplo no está pensado para procesar archivos grandes por streaming.

Dónde sirve una huella y qué comprobación falta

Integridad de archivos. Puedes comparar la huella de una descarga con una referencia obtenida de un canal confiable. Si un atacante puede sustituir el archivo y el hash publicado junto a él, la comparación por sí sola no te protege. El origen de la referencia forma parte del problema.

Firmas digitales. Las funciones hash participan en esquemas de firma para vincular una firma con un mensaje. Calcular un hash no equivale a firmar: hacen falta el algoritmo de firma, las claves y una verificación correcta. NIST especifica los esquemas de firma en FIPS 186-5.

Autenticación de mensajes. Si dos partes comparten una clave y necesitan comprobar autenticidad e integridad, un mecanismo como HMAC combina una función hash y una clave siguiendo una construcción definida. No lo sustituyas por una concatenación casera de secreto y mensaje. NIST: HMAC.

Identificación por contenido. Un sistema puede usar digests para localizar contenido o detectar posibles duplicados. Debe definir qué bytes representa cada identificador y qué hará ante una coincidencia, según el impacto de confundir dos objetos. El hash no decide esas reglas de negocio.

Por qué no guardar contraseñas con SHA-256 a secas

SHA-256 está diseñado para calcularse eficientemente. Eso resulta útil para verificar datos, pero permite que quien roba una base de hashes pruebe muchos candidatos a contraseña. Las contraseñas humanas no se eligen uniformemente entre todas las entradas posibles.

Añadir un salt evita reutilizar ciertos cálculos entre cuentas, pero no convierte un hash rápido en una función adecuada para contraseñas. Utiliza una función específica con un coste configurable, como Argon2id, mediante una implementación mantenida. OWASP explica estas diferencias en su Password Storage Cheat Sheet.

SHA-256 no es «criptográficamente inseguro en general». Es una herramienta inapropiada por sí sola para este objetivo. La guía de almacenamiento seguro de contraseñas desarrolla salt, costes y verificación sin convertir este artículo en otro tutorial de autenticación.

Una huella necesita contexto

Vuelve al playground e introduce el mismo texto en ambos campos. Después añade un único espacio a uno de ellos. El cambio visual ayuda a recordar la regla operativa: el hash identifica una secuencia de bytes bajo un algoritmo determinado, no el significado que una persona atribuye al texto.

Cuando uses SHA-256 en una aplicación, define la codificación, conserva el algoritmo junto al formato del dato y aclara qué estás comprobando. Comparar integridad, autenticar un mensaje y proteger una contraseña son decisiones distintas, aunque en todas aparezcan cadenas que parecen hashes.

Referencias

#SHA-256#funciones hash#efecto avalancha#Web Crypto API#criptografía#JavaScript

Artículos relacionados