The go-to resource for upgrading Ruby, Rails, and your dependencies.

Weak Password Hashing in Rails: The Importance of Strong Algorithms


In the world of web application security, how you handle user passwords is one of the most critical aspects of protecting your users’ data. CWE-327: Use of a Broken or Risky Cryptographic Algorithm is a common weakness that occurs when an application uses a weak or outdated hashing algorithm to store passwords. This vulnerability can have severe consequences, as it makes it significantly easier for attackers to crack user passwords if they gain access to your database.

This article will delve into the specifics of weak password hashing in Ruby on Rails applications, explain the risks involved, and provide clear, actionable guidance on how to ensure your application is using strong, modern cryptographic algorithms.

The Dangers of Weak Password Hashing

When a user signs up for your application, you should never store their password in plain text. Instead, you should store a “hash” of the password. A hashing algorithm is a one-way function that transforms the password into a fixed-length string of characters, called a hash. When the user logs in, you hash the password they provide and compare it to the stored hash.

However, not all hashing algorithms are created equal. Older algorithms like MD5 and SHA1 were once considered secure, but advances in computing power have made them vulnerable to various attacks, including:

  • Rainbow Table Attacks: These attacks use pre-computed tables of hashes for common passwords. If your application uses a simple hashing algorithm without a “salt” (a random string added to the password before hashing), an attacker can easily look up the hash in a rainbow table and find the original password.
  • Brute-Force Attacks: With modern hardware, attackers can try billions of password combinations per second. Weak hashing algorithms are often too fast, which allows attackers to crack passwords in a very short amount of time.

Using a weak hashing algorithm is like locking your front door with a cheap, easily picked lock. It might deter a casual intruder, but a determined attacker will get through it with little effort.

Password Hashing in Ruby on Rails: The Right Way

Fortunately, Ruby on Rails has excellent built-in support for secure password hashing through the has_secure_password feature, which uses the bcrypt algorithm. Bcrypt is a strong, modern hashing algorithm specifically designed for passwords. It has two key features that make it a great choice:

  1. It’s slow: Bcrypt is intentionally slow, which makes brute-force attacks much more difficult and time-consuming. You can even configure the “cost factor” of bcrypt to make it slower as computing power increases over time.
  2. It’s salted: Bcrypt automatically handles salting for you. This means that even if two users have the same password, their stored hashes will be different, making rainbow table attacks ineffective.

Implementing has_secure_password

To use has_secure_password, you’ll need to do the following:

  1. Add the bcrypt gem to your Gemfile:

    # Gemfile
    gem 'bcrypt'

    Then, run bundle install.

  2. Add a password_digest column to your users table:

    You can do this by creating a new migration:

    rails g migration AddPasswordDigestToUsers password_digest:string

    And then running the migration:

    rails db:migrate
  3. Add has_secure_password to your User model:

    # app/models/user.rb
    class User < ApplicationRecord
      has_secure_password
    end

That’s it! Now, when you create a new user with a password and password_confirmation, Rails will automatically hash the password using bcrypt and store it in the password_digest column.

user = User.new(
  email: "test@example.com",
  password: "password123",
  password_confirmation: "password123"
)
user.save

When a user tries to log in, you can use the authenticate method to verify their password:

user = User.find_by(email: "test@example.com")
if user&.authenticate("password123")
  # User is authenticated
else
  # Invalid credentials
end

How to Check for Weak Hashes in Your Application

If you have an older Rails application, it’s possible that it’s using a weaker hashing algorithm. Here are a few things to look for:

  • Check your Gemfile: Look for gems like devise. If you are using Devise, check its configuration in config/initializers/devise.rb to see which hashing algorithm it’s using. By default, Devise uses bcrypt, but it could have been configured to use something else.
  • Inspect your database: Look at the format of the password hashes in your users table. Bcrypt hashes have a specific format that looks something like this: $2a$12$.... If your hashes look like a simple MD5 or SHA1 hash (a long string of hexadecimal characters), you are likely using a weak algorithm.

Migrating to a Stronger Hashing Algorithm

If you discover that your application is using a weak hashing algorithm, you should migrate to bcrypt as soon as possible. Here’s a general process for doing so:

  1. Add a new password_digest column to your users table as described above.
  2. Modify your User model to support both the old and new hashing methods. You’ll need to write custom logic that checks for the presence of a password_digest. If it exists, you’ll use bcrypt for authentication. If not, you’ll fall back to the old method.
  3. When a user logs in with their old password, authenticate them using the old method. If the authentication is successful, immediately re-hash their password with bcrypt and store it in the password_digest column.
  4. Over time, as your users log in, their passwords will be migrated to the new, stronger hashing algorithm. Once all users have been migrated, you can remove the old password column and the fallback logic.

Conclusion: Prioritizing Strong Password Security

CWE-327 is a serious vulnerability that can expose your users to significant risk. As a developer, it’s your responsibility to ensure that you are using the strongest possible cryptographic algorithms to protect their data.

By using Rails’ built-in has_secure_password feature and the bcrypt algorithm, you can be confident that you are following best practices for password security. If you have an older application, take the time to audit your password hashing implementation and migrate to a stronger algorithm if necessary. Your users’ security depends on it.

Sponsored by Durable Programming

Need help maintaining or upgrading your Ruby on Rails application? Durable Programming specializes in keeping Rails apps secure, performant, and up-to-date.

Hire Durable Programming