class Sorcery::CryptoProviders::BCrypt
For most apps Sha512 is plenty secure, but if you are building an app that stores nuclear launch codes you might want to consier BCrypt
. This is an extremely secure hashing algorithm, mainly because it is slow. A brute force attack on a BCrypt
encrypted password would take much longer than a brute force attack on a password encrypted with a Sha algorithm. Keep in mind you are sacrificing performance by using this, generating a password takes exponentially longer than any of the Sha algorithms. I did some benchmarking to save you some time with your decision:
require "bcrypt" require "digest" require "benchmark" Benchmark.bm(18) do |x| x.report("BCrypt (cost = 10:") { 100.times { BCrypt::Password.create("mypass", :cost => 10) } } x.report("BCrypt (cost = 2:") { 100.times { BCrypt::Password.create("mypass", :cost => 2) } } x.report("Sha512:") { 100.times { Digest::SHA512.hexdigest("mypass") } } x.report("Sha1:") { 100.times { Digest::SHA1.hexdigest("mypass") } } end user system total real BCrypt (cost = 10): 10.780000 0.060000 10.840000 ( 11.100289) BCrypt (cost = 2): 0.180000 0.000000 0.180000 ( 0.181914) Sha512: 0.000000 0.000000 0.000000 ( 0.000829) Sha1: 0.000000 0.000000 0.000000 ( 0.000395)
You can play around with the cost to get that perfect balance between performance and security.
Decided BCrypt
is for you? Just insall the bcrypt gem:
gem install bcrypt-ruby
Update your initializer to use it:
config.encryption_algorithm = :bcrypt
You are good to go!
Attributes
Setting the option :pepper allows users to append an app-specific secret token. Basically it's equivalent to :salt_join_token option, but have a different name to ensure backward compatibility in generating/matching passwords.
Public Class Methods
This is the :cost option for the BCrpyt library. The higher the cost the more secure it is and the longer is take the generate a hash. By default this is 10. Set this to whatever you want, play around with it to get that perfect balance between security and performance.
# File lib/sorcery/crypto_providers/bcrypt.rb, line 51 def cost @cost ||= 10 end
This method is used as a flag to tell Sorcery
to “resave” the password upon a successful login, using the new cost
# File lib/sorcery/crypto_providers/bcrypt.rb, line 73 def cost_matches?(hash) hash = new_from_hash(hash) if hash.nil? || hash == {} false else hash.cost == cost end end
Creates a BCrypt
hash for the password passed.
# File lib/sorcery/crypto_providers/bcrypt.rb, line 59 def encrypt(*tokens) ::BCrypt::Password.create(join_tokens(tokens), cost: cost) end
Does the hash match the tokens? Uses the same tokens that were used to encrypt.
# File lib/sorcery/crypto_providers/bcrypt.rb, line 64 def matches?(hash, *tokens) hash = new_from_hash(hash) return false if hash.nil? || hash == {} hash == join_tokens(tokens) end
# File lib/sorcery/crypto_providers/bcrypt.rb, line 82 def reset! @cost = 10 @pepper = '' end
Private Class Methods
# File lib/sorcery/crypto_providers/bcrypt.rb, line 89 def join_tokens(tokens) tokens.flatten.join.concat(pepper.to_s) # make sure to add pepper in case tokens have only one element end
# File lib/sorcery/crypto_providers/bcrypt.rb, line 93 def new_from_hash(hash) ::BCrypt::Password.new(hash) rescue ::BCrypt::Errors::InvalidHash nil end