Barong since 2.0 version use OpenBSD bcrypt() password hashing algorithm, that allow us easily store a secure hash of users' passwords.
As a base Barong takes bcrypt-ruby gem - Ruby binding for the OpenBSD bcrypt() With rails 5 has_secure_password it gives us full power of algorithm
Hash algorithms take a chunk of data (e.g., user's password) and create a "digital fingerprint," or hash, of it. Because this process is not reversible, there's no way to go from the hash back to the password.
In other words:
hash(p) #=> <unique gibberish>
We store the hash and check it against a hash made of a potentially valid password:
<unique gibberish> =? hash(just_entered_password)
But even this has weaknesses -- attackers can just run lists of possible passwords through the same algorithm, store the results in a big database, and then look up the passwords by their hash:
PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
Our solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:
hash(salt + p) #=> <really unique gibberish>
The salt is then stored along with the hash in the database, and used to check potentially valid passwords:
<really unique gibberish> =? hash(salt + just_entered_password)
bcrypt-ruby automatically handles the storage and generation of these salts for you.
Adding a salt means that an attacker has to have a gigantic database for each unique salt -- for a salt made of 4 letters, that's 456,976 different databases. Pretty much no one has that much storage space, so attackers try a different, slower method -- throw a list of potential passwords at each individual password:
hash(salt + "aadvark") =? <really unique gibberish>
hash(salt + "abacus") =? <really unique gibberish>
etc.
This is much slower than the big database approach, but most hash algorithms are pretty quick -- and therein lies the
problem. Hash algorithms aren't usually designed to be slow, they're designed to turn gigabytes of data into secure
fingerprints as quickly as possible. bcrypt()
, though, is designed to be computationally expensive:
Ten thousand iterations:
user system total real
md5 0.070000 0.000000 0.070000 ( 0.070415)
bcrypt 22.230000 0.080000 22.310000 ( 22.493822)
If an attacker was using Ruby to check each password, they could check ~140,000 passwords a second with MD5 but only
~450 passwords a second with bcrypt()
.
bcrypt()
is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating
system available.
For a more technical explanation of the algorithm and its design criteria, please read Niels Provos and David Mazières' Usenix99 paper: https://www.usenix.org/events/usenix99/provos.html
If you'd like more down-to-earth advice regarding cryptography, I suggest reading Practical Cryptography by Niels Ferguson and Bruce Schneier: https://www.schneier.com/book-practical.html