As discussed in the password security primer, storing user passwords as cleartext in the database is a very bad idea. The primary way to prevent this is to use cryptographic hashes. Ruby has a number of ways to generate cryptographic hashes.
Note that these are cryptographic hashes, not key/value hashes. Cryptographic hashing algorithms calculate unique numbers to determine the integrity or identity of a string, file or any other bit stream. They have little to nothing to do with Ruby's Hash class.
The Digest Module
The Digest module is part of the Ruby standard library. It doesn't provide a very wide variety if hashing algorithms (more or less, just SHA1 and SHA2) but it should be available on all Ruby installations. To begin using it, require 'digest'. You'll then have a number of algorithms available via the Digest::SHA1 and the various SHA2 classes: Digest::SHA2, Digest::SHA256, Digest::SHA384 and Digest::SHA512.
Each of these classes has a class method that does what most people will want: give the hex-encoded hash of a string. Remember that hashes are binary strings, a "raw" hash will contain characters that aren't printable, or may send control codes to terminals you try to print them to. For this reason, hashes are either encoded in Base64 or more commonly, in hex. The hash is a just a very large integer, representing it in hex is a natural choice. The method to quickly make hex strings of the hashes of Ruby strings is the hexdigest string. Since it's a class method, you don't need to instantiate any objects or keep any references. This is just a method that takes a single argument and returns a single value.
This is an example of this usage, it really doesn't get any simpler than this.
#!/usr/bin/env ruby require 'digest' password = "A user's password" hash = Digest::SHA1.hexdigest(password) puts hash # This will produce the hash # 62018390552aaba3d344e3b43bfa14e49e535dfc
Another common use for hashes is to check file integrity. Though that's not a security measure, just to make sure a download was successful. If you need file authenticity checking, use digital signatures instead. In order to check file integrity with the method above, you would have to load the entire file into memory. That's OK for small files, but for large files that's certainly not a good idea. But there's another interface to these classes that allows you to gradually build hashes from a data stream. We'll be using the Digest::SHA512 class here, but they all have the same interface. To make a hash, you'll be creating an instance of this class, reading chunks of data and passing it to the update method. Whenever you want the hash, you can read it from the hexdigest method or simply from its to_s method.
#!/usr/bin/env ruby # Computes the SHA1 hash of any file (passed # on the command line) require 'digest' sha1 = Digest::SHA1.new File.open(ARGV) do|file| buffer = '' # Read the file 512 bytes at a time while not file.eof file.read(512, buffer) sha1.update(buffer) end end puts sha1 # Implicitly calls to_s
The Digest classes that come with the Ruby standard library leave a bit to be desired. They only support a few algorithms and no MD5, and older and weaker but popular hashing algorithm. Thankfully, most Ruby installations come with OpenSSL support. OpenSSL is a cryptographic library that supports a wide variety of hashing algorithms, chances are it will support everything you need.
Its usage is similar to the Digest classes. You can either make a digest using a class method or instantiate the various hashing classes and build a hash over time. The classes themselves are named like OpenSSL::Digest::SHA1, a full list can be found in the documentation.
In this first example, we'll generate a hash on the fly, without instantiating any of the digest objects.
require 'openssl' OpenSSL::Digest::MD5.hexdigest('test') # => "098f6bcd4621d373cade4e832627b4f6"
As you can see, no big surprises here. This is more or less the same interface as with the Digest::* classes. Similarly, almost the same code can be used for building hashes incrementally.
#!/usr/bin/env ruby # Computes the SHA1 hash of any file (passed # on the command line) require 'openssl' sha1 = OpenSSL::Digest::SHA1.new File.open(ARGV) do|file| buffer = '' # Read the file 512 bytes at a time while not file.eof file.read(512, buffer) sha1.update(buffer) end end puts sha1 # Implicitly calls to_s
Compare this with the previous example. The interface is completely compatible in this case. The only changes here are changing the require and instantiation lines a bit.