Digest — Hashing with MD5, SHA256 and More
Hashing turns any chunk of data — a password, a file, a JSON payload — into a fixed-length fingerprint. The same input always produces the same output, but there’s no way to reverse it. Ruby’s standard library ships with this capability through the Digest module, which wraps a range of hash functions from OpenSSL underneath.
You don’t need any gems for this. require 'digest' is all it takes.
One-Shot Hashing
The quickest way to hash something is with the class method:
require 'digest'
Digest::SHA256.hexdigest("hello")
# => "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
hexdigest returns a human-readable hex string. If you’d rather work with the raw binary bytes, use digest:
Digest::SHA256.digest("hello")
# => "\xaci\r\xba_\xb0\xa3\x0e&S\xb2\xac[~\x92\xe1\xb1a..."
For base64 encoding (useful in HTTP headers or JSON payloads):
Digest::SHA256.base64digest("hello")
# => "rMhdrF+woD4m46K8W56S4xsYXsH6dCXF6zBDYimL6Jg="
Available Algorithms
The module provides one class per algorithm:
Digest::MD5.hexdigest("hello") # "5d41402abc4b2a76b9719d911017c592" (insecure, do not use)
Digest::SHA1.hexdigest("hello") # "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" (insecure, do not use)
Digest::SHA256.hexdigest("hello") # the SHA256 example above
Digest::SHA384.hexdigest("hello")
Digest::SHA512.hexdigest("hello")
Digest::RMD160.hexdigest("hello") # RIPEMD-160
MD5 and SHA1 are cryptographically broken and should never be used for security-sensitive work. SHA256 is the minimum recommended for new code. SHA512 is fine if you prefer a longer hash.
Incremental Hashing with update
When your data arrives in chunks — a streaming file read, a large payload — build the hash incrementally:
sha = Digest::SHA256.new
sha.update("hello")
sha.update(" ")
sha.update("world")
sha.hexdigest
# => "b3d9e23f4c7e2f1a8c6b5d4e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a"
The << operator is an alias for update, so you can chain more elegantly:
sha = Digest::SHA256.new
sha << "hello" << " " << "world"
sha.hexdigest
Once you’re done, call reset to clear the state for a fresh round:
sha.reset
sha << "new data"
sha.hexdigest
Hashing Files
Hashing a file without loading it entirely into memory uses file:
sha = Digest::SHA256.file("/path/to/large.zip")
sha.hexdigest
Or more concisely as a one-liner:
Digest::SHA256.file("/path/to/large.zip").hexdigest
file reads in chunks, so it works on arbitrarily large files without blowing up your memory.
Checking Equality with ==
Compare a digest object directly against a string:
sha = Digest::SHA256.file("/etc/hosts")
sha == "a7b3c2d1e9f0..." # false if mismatched
This is handy for password verification in a login flow:
def verify_password(input, stored_hash)
Digest::SHA256.hexdigest(input) == stored_hash
end
Though for actual passwords, you should use a dedicated key derivation function like bcrypt or Argon2 instead of a plain hash — those are designed to be slow to resist brute-force attacks.
Choosing an Algorithm
SHA256 is the default recommendation for most uses. It strikes a balance between speed and security for non-cryptographic applications like checksums and content-addressable storage.
SHA512 is preferred when you want a longer hash or are working in an environment where SHA256’s 32-byte output feels tight. SHA384 is essentially SHA512 truncated, if you want a shorter output without switching algorithms.
MD5 and SHA1 remain useful only for legacy compatibility — for example, validating the checksum of an old download that was published with an MD5 hash. Never use them for new security work.
Common Pitfalls
Reusing a digest object without resetting. After calling hexdigest, the digest state still holds the accumulated data. Call reset before starting a new hash:
# Wrong — second call hashes nothing
sha = Digest::SHA256.new
sha << "hello"
sha.hexdigest # correct
sha.hexdigest # wrong — same result
# Right
sha.reset
sha << "world"
sha.hexdigest # correct
Storing passwords as plain hashes. A SHA256 hash of a password can be cracked with a dictionary attack in minutes. Use bcrypt or Argon2 with built-in salting instead.
See Also
- ruby-string-manipulation — string transformation techniques useful when preparing data for hashing
- ruby-blocks-procs-lambdas — chaining operations with blocks and procs
- ruby-io-and-files — handling exceptions when working with files and digests