Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Super easy password hashing and salting for ActiveRecord (Rails)

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 spec
Octocat-spinner-32 .gitignore
Octocat-spinner-32 Gemfile
Octocat-spinner-32 README.textile
Octocat-spinner-32 Rakefile
Octocat-spinner-32 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.