Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Super easy password hashing and salting for ActiveRecord (Rails)
Ruby
Branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib Requiring at least Rails 3.0.4 (doesn't work in earlier versions beca…
spec Added helpful error to prevent validating against HashedString
.gitignore Generated gem skeleton
Gemfile Adjusted dependencies
README.textile
Rakefile
salt-and-pepper.gemspec

README.textile

Salt and Pepper

Provides automatic password hashing for ActiveRecord (>= 3.0.4) and a couple of methods for generating random strings, tokens, etc.
Features:

  • Mark columns for auto-hashing with a single line of code.
  • Automatic salting of hashes. No separate column is required for the salt.
  • Does not break validations on the hashed columns (only a small change is required).
  • Super easy hash verification.
  • Tested using RSpec 2

Digest::SHA256 is used for hashing and ActiveSupport::SecureRandom is used for generating random stuff.

Installation

Just add it to your Gemfile and run bundle install:

gem "salt-and-pepper"

Note: Rails 3.0.4 or higher is required.

Usage

To enable automatic hashing for a column, call hash_column in your model:

class User < ActiveRecord::Base
  hash_column :password
end

You can specify multiple columns in one line or in separate lines:

class User < ActiveRecord::Base
  hash_column :password, :security_token

  # or
  hash_column :password
  hash_column :security_token
end

Options

You can pass the :length option to change the length of the stored hash.
Numbers between 96 and 192 are accepted, the default value is 128. Make sure the database column can store a string that long!

# set length for both columns
hash_column :password, :security_token, :length => 100

# or adjust them individually
hash_column :password, :length => 160
hash_column :secret, :length => 120

By default, blank (= empty or whitespace-only) strings will be converted to nil, and will not be hashed.
If you really want blank strings to be hashed, use the :hash_blank_strings option:

# Default behavior:
#                    nil => nil
#           empty string => nil
# whitespace-only string => nil

hash_column :password, :hash_blank_strings => true

# New behavior:
#                    nil => nil
#           empty string => 77c0a93ad8e5f42cf676...
# whitespace-only string => 1b8d091174299844b1a4...

Verification

Just compare the two values:

if @user.password == "secret"
  # password is valid
end

A full example:

# app/models/user.rb
class User < ActiveRecord::Base
  hash_column :password
end

# app/controllers/sessions_controller.rb
def create
  @user = User.find_by_username(params[:username])
  if @user.present? && @user.password == params[:password]
    # login user here
  else
    redirect_to new_session_path, :alert => "Invalid username or password."
  end
end

Validating hashed columns

Salt and Pepper provides the validate_[column]? method for deciding whether validations on the column should be performed.
Use it to prevent running your sophisticated length-checking algorithms on a 128-character hash :). Skipping validation of hashed values is safe because they were already checked at the time they were set.

class User < ActiveRecord::Base
  encrypt :password
  validates :password, :length => { :within => 6..100 }, :if => :validate_password?
end

Generating random stuff

Salt and Pepper has a couple of handy methods for generating random numbers, codes, tokens, etc:

SaltPepper.number(6)                                  # => 4     (identical to: 0..5)
SaltPepper.number(10..20)                             # => 11
SaltPepper.alpha_code                                 # => "SNPBJSDG"
SaltPepper.alpha_code(4)                              # => "FKNP"
SaltPepper.numeric_code                               # => "01570475"
SaltPepper.numeric_code(20)                           # => "70110124996934848762"
SaltPepper.code                                       # => "29Y3WSEC"     (alphanumeric)
SaltPepper.code(5)                                    # => "89U1F"
SaltPepper.code(10, 'a'..'z')                         # => "mqxeozlelw"
SaltPepper.code(15, (0..1).to_a + ('a'..'b').to_a)    # => "0ab1b0b1b01a0a1"
SaltPepper.token                                      # => "a0d5828f79e9e22dbc1f896e49f8183a"
SaltPepper.token(16)                                  # => "caa4a085edb19499"

License

Released under the MIT license.
Copyright © Máté Solymosi 2011

Something went wrong with that request. Please try again.