Verificación segura de archivos con Ruby y SHA384
Garantizar la integridad de los archivos es fundamental para los desarrolladores, sobre todo al manejar datos sensibles o distribuir software. El hashing SHA384 ofrece un método sólido para verificar que los archivos no se hayan alterado ni corrompido. Veamos cómo puedes implementar hashing SHA384 en Ruby con la biblioteca OpenSSL.
¿Por qué SHA384?
SHA384 forma parte de la familia SHA-2 y ofrece un algoritmo de hashing seguro que genera un hash único de 384 bits (48 bytes). Se usa ampliamente para verificar la integridad de los archivos y asegurar que los datos no se hayan manipulado durante las transferencias o el almacenamiento.
El hashing SHA384 mejora la seguridad de los archivos al:
- Detectar corrupción de archivos o modificaciones no autorizadas.
- Proporcionar una huella única para cada archivo.
- Garantizar la integridad de los datos durante las transferencias.
- Ofrecer mayor seguridad que SHA256 con un impacto mínimo en el rendimiento.
Configurar tu entorno de Ruby
La integración de OpenSSL en Ruby viene incorporada, pero necesitarás una instalación de Ruby adecuada. Así es como configurarla con rbenv:
En macOS o Linux con Homebrew
brew install rbenv
rbenv init
# Follow the printed Instructions to update your shell configuration
En Linux sin Homebrew
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
~/.rbenv/bin/rbenv init
# Follow the printed Instructions to update your shell configuration
Tras la instalación, instala Ruby:
rbenv install 3.2.2 # or latest stable version
rbenv global 3.2.2
ruby -v # Verify installation
Hashing de archivos con Ruby y OpenSSL
Esta es una implementación sólida para calcular el hash de archivos con la biblioteca OpenSSL integrada en Ruby:
require 'openssl'
def sha384_hash(file_path)
digest = OpenSSL::Digest.new('SHA384')
File.open(file_path, 'rb') do |file|
buffer = ""
# Read file in chunks to handle large files efficiently
digest.update(buffer) while file.read(8192, buffer)
end
digest.hexdigest
end
# Usage example with error handling
begin
hash = sha384_hash('example.txt')
puts "SHA384 hash: #{hash}"
rescue Errno::ENOENT
puts "Error: File not found"
rescue => e
puts "Error: #{e.message}"
end
Esta implementación:
- Lee el archivo por fragmentos (8 KB cada vez), lo que permite manejar archivos grandes de forma eficiente sin cargarlos por completo en memoria.
- Usa el modo binario (
'rb') para leer archivos y así obtener resultados consistentes en todas las plataformas. - Incluye un manejo adecuado de errores para problemas habituales, como archivos inexistentes.
Verificar la integridad de los archivos
Para verificar la integridad de un archivo, compara el hash generado con un hash conocido como válido. Es fundamental usar una función de comparación de tiempo constante para evitar ataques de temporización, en los que un atacante podría deducir información sobre el hash midiendo el tiempo que tarda en completarse la comparación.
require 'openssl'
# Constant-time comparison function
def secure_compare(a, b)
# Ensure strings are of the same type and encoding for accurate byte comparison
a = a.b unless a.encoding == Encoding::BINARY
b = b.b unless b.encoding == Encoding::BINARY
return false unless a.bytesize == b.bytesize
l = a.unpack "C*"
res = 0
b.each_byte { |byte| res |= byte ^ l.shift }
res == 0
end
# Assume sha384_hash function from previous example is defined
expected_hash = "your_known_good_hash_here" # Replace with the actual expected hash
begin
file_hash = sha384_hash('example.txt')
if secure_compare(file_hash, expected_hash)
puts "File integrity verified."
else
puts "File integrity compromised!"
end
rescue Errno::ENOENT
puts "Error: File not found"
rescue => e
puts "Error verifying file: #{e.message}"
end
Consideraciones multiplataforma
Al verificar archivos en distintos sistemas operativos, ten en cuenta las posibles diferencias de fin de línea (CRLF en Windows frente a LF en macOS/Linux) que pueden afectar a los valores hash si los archivos se leen en modo texto. Usa siempre el modo binario al leer archivos para calcular su hash y así obtener resultados consistentes:
# Always use binary mode ('rb') for consistent cross-platform results
File.open(file_path, 'rb') do |file|
# Hashing logic here...
end
Si no te queda más remedio que comparar hashes de archivos de texto generados en distintas plataformas y no puedes garantizar que se usara el modo binario desde el principio, podrías plantearte normalizar los finales de línea antes de calcular el hash. Sin embargo, la mejor práctica es calcular siempre el hash del archivo byte a byte en modo binario.
# Example of normalizing line endings (use with caution)
def normalize_and_hash(file_path)
content = File.read(file_path, mode: 'rb')
# Normalize to LF line endings
normalized = content.gsub(/\r\n?/, "\n")
digest = OpenSSL::Digest.new('SHA384')
digest.update(normalized)
digest.hexdigest
end
Automatizar la verificación de archivos
Automatizar la verificación de archivos puede agilizar tu flujo de trabajo, sobre todo cuando trabajas con varios archivos, por ejemplo durante la distribución de software o en pipelines de validación de datos. Aquí tienes un script para automatizar la verificación de varios archivos frente a una lista de hashes conocidos:
require 'openssl'
# Assume sha384_hash and secure_compare functions are defined as above
files_to_verify = {
'example.txt' => 'known_good_hash_for_example_txt',
'another_file.dat' => 'known_good_hash_for_another_file'
# Add more files and their expected hashes here
}
files_to_verify.each do |file, expected_hash|
begin
actual_hash = sha384_hash(file)
if secure_compare(actual_hash, expected_hash)
puts "#{file}: Verified ✅"
else
puts "#{file}: Verification failed ❌ (Hash mismatch)"
end
rescue Errno::ENOENT
puts "#{file}: Verification failed ❌ (File not found)"
rescue => e
puts "#{file}: Error - #{e.message} ❌"
end
end
Solución de problemas habituales
Al trabajar con hashing SHA384 en Ruby, es posible que te encuentres con estos problemas comunes:
- Hashes inconsistentes entre plataformas: asegúrate de usar el modo binario (
'rb') al abrir archivos para calcular su hash. Las diferencias de fin de línea en modo texto alterarán el hash. - Problemas de memoria con archivos grandes: comprueba que estés usando el enfoque de lectura
por fragmentos (leer el archivo poco a poco) como se muestra en el ejemplo de
sha384_hash, en lugar de cargar el archivo completo en memoria. - Comparaciones de hash incorrectas: usa una función de comparación de tiempo constante (
secure_compare) para evitar ataques de canal lateral por temporización. Una comparación simple de cadenas (==) puede ser vulnerable. - Problemas de representación de caracteres: aunque el modo binario mitiga la mayoría de los problemas de representación de caracteres al calcular hashes, ten cuidado si manipulas el contenido del archivo como cadenas antes de hacerlo. Mantén una representación consistente.
- Problemas de rendimiento: para archivos extremadamente grandes o escenarios de alto rendimiento, las extensiones nativas o las herramientas externas podrían ofrecer mejor rendimiento, aunque el binding de OpenSSL de Ruby suele ser eficiente.
Buenas prácticas para el manejo y la verificación seguros de archivos
- Usa siempre el modo binario: lee los archivos en modo binario (
File.open(file_path, 'rb')) para calcular hashes y así garantizar la consistencia entre plataformas y evitar problemas con los finales de línea o la representación de caracteres. - Procesa los archivos grandes por fragmentos: evita cargar archivos grandes enteros en
memoria. Usa lectura con búfer (por ejemplo,
file.read(chunk_size, buffer)) para gestionar la memoria de forma eficiente. - Implementa un manejo adecuado de errores: anticipa problemas como archivo no encontrado
(
Errno::ENOENT), errores de permisos o errores de E/S, y trátalos con elegancia. - Guarda los hashes de forma segura: mantén los hashes conocidos como válidos separados de los archivos que verifican. Guárdalos en una base de datos segura, un archivo de configuración o un manifiesto firmado.
- Usa comparación de tiempo constante: al verificar hashes, usa siempre una función de comparación de cadenas de tiempo constante para evitar ataques de temporización.
- Mantén las bibliotecas actualizadas: asegúrate de que tu instalación de Ruby y su biblioteca OpenSSL estén al día para beneficiarte de los últimos parches de seguridad.
- Piensa en la fuente de verdad: establece un proceso seguro para generar y distribuir los hashes iniciales «conocidos como válidos».
Usar SHA384 con Transloadit
El ecosistema de Robots de Transloadit incluye el 🤖 /file/hash Robot como parte de nuestro servicio de catalogación de medios. Este Robot admite varios algoritmos de hashing, incluido SHA384, lo que te permite generar hashes como parte de tus flujos de trabajo de procesamiento de archivos. Aquí tienes un ejemplo de Assembly Instruction:
{
"steps": {
"hash": {
"robot": "/file/hash",
"use": ":original",
"algorithm": "sha384"
}
}
}
Al integrar el hashing SHA384 en tus aplicaciones de Ruby, añades una sólida capa de seguridad que garantiza que tus archivos permanezcan íntegros y a prueba de manipulaciones durante todo su ciclo de vida.
