Verificación segura de archivos con Ruby y SHA384
Para verificar una descarga en Ruby, calcula el hash de sus bytes con SHA384 de OpenSSL y compara el resultado con una suma de verificación de confianza. El siguiente script acepta uno o más pares de archivo y suma de verificación y devuelve un estado de salida distinto de cero si falla alguna verificación, por lo que puedes usarlo en una tarea automatizada.
Empieza con una suma de verificación de confianza
SHA-384 pertenece a la familia SHA-2 y genera un resumen de 384 bits: 48 bytes o 96 caracteres hexadecimales. Úsalo cuando el responsable de la publicación o tu flujo de trabajo actual proporcione sumas de verificación SHA384; una referencia generada con SHA256 no se puede verificar con SHA384.
Obtén la suma de verificación esperada de una fuente de confianza, como la página HTTPS autenticada de la versión del responsable de la publicación o un manifiesto de sumas de verificación firmado cuya firma hayas verificado. Si alguien puede reemplazar tanto el archivo como su suma de verificación de referencia, puede hacer que tu verificación pase. El cálculo del hash detecta cambios respecto a esa referencia; no hace que un archivo sea inviolable ni establece que sea seguro ejecutarlo. La documentación de OpenSSL de Ruby explica cómo los resúmenes también forman parte de las firmas digitales.
Comprueba Ruby y OpenSSL
Necesitas Ruby con su extensión OpenSSL. Los comandos de este documento se probaron en Linux con
Ruby 3.4.10, Ruby/OpenSSL 3.3.3 y OpenSSL 3.6.4. Los ejemplos de shell usan Bash;
sha384sum de GNU Coreutils es opcional para la comparación independiente que
aparece más adelante. Si no tienes Ruby, sigue la
guía oficial de instalación de Ruby para tu plataforma.
Comprueba el intérprete, la biblioteca enlazada y la disponibilidad de SHA384 antes de continuar:
ruby -ropenssl -e 'puts RUBY_DESCRIPTION; puts "Ruby/OpenSSL #{OpenSSL::VERSION}"; puts OpenSSL::OPENSSL_LIBRARY_VERSION; puts "SHA384: #{OpenSSL::Digest.new("SHA384").digest_length} bytes"'
La última línea debería ser SHA384: 48 bytes. Si falla la carga de OpenSSL o la
creación del resumen, corrige tu instalación de Ruby antes de ejecutar el verificador. El comando
openssl de tu PATH puede usar una biblioteca distinta de la que usa Ruby,
por lo que su versión por sí sola no demuestra que este requisito funcione.
Guarda el verificador
Guarda este script completo como verify_sha384.rb. Lee cada archivo en bloques de
8 KiB y reutiliza el búfer, por lo que no carga todo el archivo en memoria. El método de Ruby
IO#read devuelve
nil al llegar al final del archivo cuando se le proporciona una longitud
positiva, incluso si el archivo está vacío. Cada bloque se pasa a
OpenSSL::Digest#update.
require 'openssl'
def sha384_hash(path)
digest = OpenSSL::Digest.new('SHA384')
File.open(path, 'rb') do |file|
buffer = ''.b
digest.update(buffer) while file.read(8192, buffer)
end
digest.hexdigest
end
if ARGV.empty? || ARGV.length.odd?
warn 'Usage: ruby verify_sha384.rb FILE SHA384 [FILE SHA384 ...]'
exit 2
end
failed = false
ARGV.each_slice(2) do |path, expected|
unless /\A[0-9a-fA-F]{96}\z/.match?(expected)
warn "#{path}: ERROR (expected 96 hexadecimal characters)"
failed = true
next
end
begin
actual = sha384_hash(path)
if actual == expected.downcase
puts "#{path}: OK"
else
warn "#{path}: FAILED (SHA384 mismatch)"
failed = true
end
rescue SystemCallError, IOError => error
warn "#{path}: ERROR (#{error.message})"
failed = true
end
end
exit(failed ? 1 : 0)
Pasa solo el resumen, sin nombre de archivo, prefijo sha384- ni espacios en
blanco a su alrededor. Se aceptan letras hexadecimales mayúsculas. El script compara sumas de
verificación públicas con ==; en este flujo de trabajo no hay ninguna
suma de verificación secreta que proteger frente a su divulgación mediante el análisis de tiempos.
Para autenticadores secretos como los HMAC, Ruby proporciona
métodos de comparación en tiempo constante
en lugar de exigir un bucle de comparación escrito a mano.
Verifica un archivo con un resultado conocido
En un directorio de pruebas que contenga verify_sha384.rb, ejecuta los siguientes
comandos en Bash. Crean example.txt con exactamente los tres bytes
abc, sin salto de línea al final. La creación no permite sobrescribir
un archivo existente; si falla, la cadena && se detiene antes de la
verificación. Usa otro directorio de pruebas si quieres repetir la preparación.
ruby -e 'File.open("example.txt", "wbx") { |file| file.write("abc") }' &&
EXPECTED_SHA384='cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7' &&
ruby verify_sha384.rb example.txt "$EXPECTED_SHA384"
El resultado es:
example.txt: OK
La ejecución termina con el estado 0. Para tu propia descarga,
sustituye la ruta y la suma de verificación SHA384 de confianza por las de esa versión exacta.
Pon entre comillas las rutas que contengan espacios. El verificador abre los archivos solo para
lectura y no crea ningún artefacto de verificación.
Si tienes GNU Coreutils, calcula de forma independiente la suma de verificación del archivo de
muestra con sha384sum:
sha384sum --binary -- example.txt
Su primer campo debería ser igual a EXPECTED_SHA384. Calcular el hash de un archivo
que acabas de descargar es útil para comparar, pero por sí solo no te proporciona una referencia
de confianza para esa descarga.
Haz que un lote fallido provoque el fallo de la tarea
Sigue usando la misma sesión de Bash para que EXPECTED_SHA384 conserve su valor.
Crea un archivo vacío y verifica ambas muestras pasando otro par de archivo y suma de verificación:
ruby -e 'File.open("empty.bin", "wbx") {}' &&
EMPTY_SHA384='38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b' &&
ruby verify_sha384.rb example.txt "$EXPECTED_SHA384" empty.bin "$EMPTY_SHA384"
Ambos archivos deberían mostrar OK, y el comando termina con
0. La creación de empty.bin tampoco permite un
destino existente. Ahora añade un salto de línea al primer archivo de muestra desechable y repite
el lote:
ruby -e 'File.open("example.txt", "ab") { |file| file.write("\n") }' &&
ruby verify_sha384.rb example.txt "$EXPECTED_SHA384" empty.bin "$EMPTY_SHA384"
example.txt muestra FAILED (SHA384 mismatch) en la salida de error
estándar, mientras que empty.bin sigue mostrando
OK en la salida estándar. El lote termina con
1: un éxito posterior no puede borrar un fallo anterior.
El script verifica cada par proporcionado y no reescribe ninguna de las dos sumas de verificación
de referencia.
Los códigos de salida son 0 cuando todos los archivos coinciden,
1 cuando algún archivo no coincide, tiene una suma de verificación
esperada con formato incorrecto o no se puede leer, y 2 cuando falta
la lista de pares o está incompleta. En una tarea, haz que el verificador sea el último comando de
su paso o propaga explícitamente su estado distinto de cero antes de ejecutar comandos posteriores.
Mostrar un mensaje de error por sí solo no hace que una tarea de shell falle.
Diagnostica una verificación fallida
- Se esperaban 96 caracteres hexadecimales: copia solo el resumen SHA384. Una suma de
verificación SHA256, un valor Base64 o una línea completa de la salida de
sha384sumno es un argumento válido. - SHA384 no coincide: comprueba la versión, el nombre del archivo, que la descarga esté completa y el algoritmo esperado. No reemplaces la referencia por el nuevo hash solo para que la verificación pase.
- Error de archivo o permisos: comprueba el directorio de trabajo, la ruta y los permisos de
lectura de la cuenta que ejecuta la tarea. Una lectura fallida devuelve
1aunque otros archivos pasen la verificación.
Conserva los bytes que quieres verificar
El modo binario conserva los bytes proporcionados para calcular el resumen. La
documentación de modos de archivo de Ruby
describe cómo 'b' desactiva la conversión de saltos de línea de Windows.
Esto no hace que un archivo LF y uno CRLF sean idénticos: esos archivos contienen bytes distintos
y deberían tener sumas de verificación distintas. Evita normalizar los finales de línea, recortar
espacios en blanco o cambiar la representación de caracteres del texto antes de esta verificación.
Esos pasos verificarían el contenido transformado en lugar del archivo descargado.
Usa archivos regulares completos que permanezcan sin cambios durante la verificación y su uso posterior. Esta pequeña CLI no bloquea los archivos ni impide que otro proceso los reemplace después de una verificación satisfactoria. Mantén protegida la referencia de confianza, así como el archivo que verificaste.
Para calcular hashes dentro de un flujo de trabajo de Transloadit, consulta la documentación de /file/hash. El mismo requisito de contar con una referencia de confianza se aplica cuando se genera una suma de verificación durante el procesamiento de archivos.
