Open-source project
bcrypt-ruby/bcrypt-ruby avatar
bcrypt-ruby/bcrypt-ruby

bcrypt-ruby: storing Ruby passwords with intentional slowness and automatic salts

bcrypt-ruby is a Ruby binding for the OpenBSD bcrypt() password hashing algorithm, allowing you to easily store a secure hash of your users' passwords.

1,994 stars293 forksCNOASSERTION

At a glance

What is it?
bcrypt-ruby is a Ruby binding for the OpenBSD bcrypt() algorithm, used directly and as the engine behind Rails has_secure_password since version 3. Its cost factor is a configurable dial between authentication speed and resistance to offline dictionary attacks, defaulting to 12.
Who is it for?
Use bcrypt-ruby when you need password hashing in a Ruby application and want the algorithm that Rails has shipped with since version 3. A default cost of 12 suits session-based logins; stateless flows such as HTTP Basic Auth need a lower value to avoid per-request latency.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 114 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why MD5's speed becomes a liability when attackers have your database

A password hashing function has one job at verification time: accept a plaintext candidate and a stored hash, and return a boolean. That job requires speed measured in milliseconds. An offline attacker doing the same job on millions of guesses has the same milliseconds available, which is the core problem with fast hash algorithms.

A benchmark in the project repository over ten thousand iterations shows MD5 finishing in 0.070415 seconds against bcrypt's 22.493822 seconds. At those speeds, an attacker using Ruby can check approximately 140,000 passwords per second with MD5 but only approximately 450 with bcrypt. Niels Provos and David Mazières of the OpenBSD Project designed bcrypt to be computationally expensive on purpose, and that gap is the protection it offers.

A second layer compounds this. Before hashing, bcrypt-ruby adds a randomly generated salt to each password, so two users with the same password get different hash strings. An attacker building precomputed rainbow tables would need a separate table for each unique salt. With a four-letter salt, that amounts to 456,976 different databases. bcrypt-ruby generates and stores the salt automatically, embedding it in the output hash string alongside the algorithm version and cost factor, so the application never handles the salt as a separate value.

gem install bcrypt requires a C compiler on Unix and the DevKit on Windows

Installing the gem on a Unix system with a C compiler takes one command:

bash
gem install bcrypt

Three platform categories are supported: JRuby, RubyInstaller builds on Windows with the DevKit, and any modern Ruby on a BSD, OS X or Linux system with a C compiler. Because the gem includes a C extension compiled at install time, both the Windows and Unix paths require a compilation tool. A Windows setup without the DevKit will fail when the native extension tries to build.

At the top level, the repository includes an ext/ directory and a bcrypt.gemspec, which confirms that the C extension is part of the source tree. No minimum Ruby version appears in the project documentation. Pre-compiled binaries for any platform, a Docker image, and a pure-Ruby fallback are all absent. An application server without a C toolchain and not running JRuby cannot install the gem as described.

One platform consideration worth reviewing before choosing a runtime: JRuby builds at version 2.1.3 and below have a historical vulnerability described in a section below. If you are deciding between MRI Ruby and JRuby for a deployment that will use this gem, that section is relevant before you commit.

BCrypt::Password.create returns a string that carries its own version, cost and salt

Two methods cover the main workflow: BCrypt::Password.create for hashing a new password, and BCrypt::Password.new for wrapping a stored hash string. From the project's own example:

ruby
require 'bcrypt'

my_password = BCrypt::Password.create("my password")
#=> "$2a$12$K0ByB.6YI2/OYrB4fQOYLe6Tv0datUVf6VZ/2Jzwm879BW5K1cHey"

my_password.version              #=> "2a"
my_password.cost                 #=> 12
my_password == "my password"     #=> true
my_password == "not my password" #=> false

Output packs the algorithm version, the cost factor, the salt and the hash into one string following the standard bcrypt format. Calling BCrypt::Password.new with that stored string reconstructs the object; the overloaded == then rehashes the candidate using the embedded salt and cost, without requiring additional state to be tracked by the application.

At upgrade time, that self-describing format has a practical benefit. Both .version and .cost allow application code to inspect existing hashes without parsing the string manually. An application raising its cost factor over time can check whether a stored hash was created at the old cost and, on a successful login, re-hash the provided password at the new cost before saving. A database column storing the hash string must be wide enough to hold the full format. From the User model example, that column is named password_hash and typed as a string.

Cost defaults to 12, and stateless authentication flows are expected to lower it

bcrypt-ruby ships with a default cost factor of 12, described in the project documentation as appropriate for session-based authentication. Stateless flows such as HTTP Basic Auth should use a lower cost to reduce server load and keep request times down. Lowering the cost factor also lowers security, but the project states there are few alternatives in that architecture.

To set a new default for all hashes created in a process:

ruby
BCrypt::Engine.cost = 8
BCrypt::Password.create('secret').cost
  #=> 8

To override on a single call without changing the global default:

ruby
BCrypt::Password.create('secret', :cost => 6).cost  #=> 6

No minimum or maximum value for the cost factor is documented. A higher cost means more time per hash and more CPU per login. It also determines how long an attacker spends on each guess of a stolen hash, so a lower value reduces both sides of that equation. Setting cost per individual user rather than globally or per call is not documented, which matters for an application that wants different cost floors for different account categories.

Rails has_secure_password has used bcrypt-ruby since version 3

Rails versions from 3 onwards ship with ActiveModel::SecurePassword, and has_secure_password uses bcrypt-ruby internally. For applications already on a supported Rails version, the password hashing layer is available without any additional gem setup beyond bcrypt-ruby being present in the Gemfile. Full interface documentation is in the Rails API docs for has_secure_password.

For applications that do not use Rails or want custom authentication, a manual approach is available:

ruby
require 'bcrypt'

class User < ActiveRecord::Base
  include BCrypt

  def password
    @password ||= Password.new(password_hash)
  end

  def password=(new_password)
    @password = Password.create(new_password)
    self.password_hash = @password
  end
end

Authenticating a user then compares a BCrypt::Password object against a plaintext string using ==:

ruby
@user = User.find_by_email(params[:email])
if @user.password == params[:password]
  give_token
end

Applications on Rails 2, projects using ActiveRecord outside Rails, or projects built on a non-ActiveRecord ORM need the manual pattern instead of has_secure_password. Sinatra, Hanami, and other frameworks are not mentioned in the project documentation.

JRuby versions at or below 2.1.3 corrupted hashes for non-ASCII passwords

A security vulnerability exists in JRuby versions of the gem at or below 2.1.3, documented with a link to a report at mindrot.org. Version 2.1.4 fixed it. Only the JRuby implementation was affected; MRI Ruby builds were not.

Passwords containing international characters were affected; those using only ASCII were not. Exposure is zero for applications whose users never set passwords with accented characters, non-Latin scripts, or characters outside the Basic Latin Unicode block. Any application that accepted non-ASCII passwords at any time during a JRuby 2.1.3-or-earlier deployment has suspect hashes for those passwords.

No automated detection path is documented. A migration utility and a flag distinguishing affected hashes from clean ones are both absent. Requiring affected users to set a new password on next login is the only safe remediation described. If the affected deployment ran years ago and the application has since moved to MRI Ruby, users who have never logged in since that migration still carry potentially corrupted hashes. No mechanism for distinguishing JRuby-written hashes from MRI-written ones is available.

Custom licence in COPYING and Argon2 as the memory-hard alternative

GitHub classifies the licence as Other because its automated detection cannot match the COPYING file to a known SPDX identifier. COPYING sits at the repository root alongside Gemfile, Rakefile, CHANGELOG and bcrypt.gemspec. Licence terms are not summarized elsewhere in the project, so reading COPYING directly is the only way to confirm what it permits before redistribution.

Release history shows recent activity. Version v3.1.22 was published on 2026-03-18, v3.1.21 on 2025-12-31, and v3.1.13 in December 2019. Between v3.1.13 and v3.1.21 there was a gap of roughly six years in tagged releases. A push to the master branch was recorded on 2026-06-19.

Argon2 is the main algorithm considered as an alternative to bcrypt in the wider security community. It adds a memory-hard component: hashing requires a configurable amount of memory, which raises the cost of GPU-parallel attacks because GPU cores have limited per-thread memory access. bcrypt-ruby does not implement Argon2. A Ruby application that requires the memory-hard property needs a separate gem. bcrypt-ruby benefits from a longer deployment record inside Rails and integration into has_secure_password; Argon2 requires evaluating available Ruby implementations separately before adoption.

Editorial conclusion

Use bcrypt-ruby when you need password hashing in a Ruby application and want the algorithm that Rails has shipped with since version 3. A default cost of 12 suits session-based logins; stateless flows such as HTTP Basic Auth need a lower value to avoid per-request latency. If your application ever ran on JRuby at version 2.1.3 or earlier and accepted non-ASCII passwords, those stored hashes are suspect and the only safe path is to require affected users to set new passwords. Read the COPYING file before redistributing the gem, because GitHub cannot match the licence to a standard identifier.

Frequently asked questions

How do I install bcrypt-ruby?

Run gem install bcrypt. On Unix-like systems a C compiler is required; on Windows the DevKit must be installed with RubyInstaller. No pre-compiled binary or pure-Ruby fallback is documented, so a JRuby runtime is the alternative when a compiler is unavailable.

What does the cost factor control in bcrypt-ruby?

It sets how computationally expensive each hash operation is. A default of 12 is described as appropriate for session-based authentication. Stateless flows such as HTTP Basic Auth should use a lower value to reduce server load, accepting reduced protection as a trade-off.

Is bcrypt-ruby safe to use on JRuby?

Current JRuby builds are safe. Versions at or below 2.1.3 had a vulnerability affecting only passwords with international characters, fixed in version 2.1.4. Any passwords hashed by a vulnerable version with non-ASCII characters need to be reset by the user.

What does BCrypt::Password.create return?

It returns a single string encoding the algorithm version, cost factor, salt and hash in the standard bcrypt format. BCrypt::Password.new accepts that string later, and the == operator rehashes the candidate using the embedded salt and cost without requiring separate state.

Does bcrypt-ruby work without Rails?

Yes. It is independent of Rails and can be used in any Ruby application. Rails has_secure_password wraps it as a convenience from version 3 onwards, but BCrypt::Password.create and BCrypt::Password.new can be called directly in any project.

Official sources

  1. bcrypt-ruby/bcrypt-ruby on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bcrypt-ruby-bcrypt-ruby.svg)](https://hysenlabs.com/projects/bcrypt-ruby-bcrypt-ruby)