Taking Password Storage Up A Notch

Taking Password Storage Up A Notch

Adam Gooch
Adam Gooch

November 04, 2012

Protect your users, take password management seriously.

There are too many databases in the world with completely unencrypted passwords. Even when databases have encrypted passwords, they typically do not implement protection that is strong enough to keep them from being stolen, and used maliciously. With the advancements in computing technology, we now need another approach to store passwords and keep them safe from prying eyes. Fortunately, you don't have to be a cryptography expert to use good password protection.

Passwords that are only hashed once are vulnerable to brute force, dictionary attacks, and rainbow tables. To combat these attacks, salt has been added to hashed passwords. However, with growing computing power, we need to go one step further. To protect our users properly, we need to increase the time it takes to encrypt passwords.

Two examples of alternate methods for password storage that solve the time problem are PBKDF2 and BCrypt. PBKDF2 stands for password based key derivation function 2. It is a replacement for an older function and is most notably used by iOS, Android, and LastPass. BCrypt is a technology used on secure operating systems such as OpenBSD. Here are the different implementations in Rails. First, the wrong way.

Plain Text (The Wrong Way)

Here is what a normal Rails model might look like without password encryption. All of the logic is in the controller to keep things simple and get the point accross.

class User < ActiveRecord::Base
		attr_accessible :email, :password, :password_confirmation
end

And this is what a normal Rails controller could look like without password encryption. The fields in the table are email, and password_digest.


class UsersController < ApplicationController
		def index
		end

		def new
				@user = User.new
		end

		def create
				@user = User.new
				@user.email = params[:user][:email]
				@user.password_digest = params[:user][:password]
				if @user.save
						redirect_to @user
				else
						render :new
				end
		end

		def show
				@user = User.find(params[:id])
		end

		def authenticate
				user = User.find_by_email(params[:email])
				if user && user.password_digest == params[:password]
						redirect_to user
				else
						render :index
				end
		end
end

Now let’s see what these would look like with enhanced encryption methods and find out how easy it is to add additional security to your application.

PBKDF2

For Rails users, there is a gem called pbkdf2 available that will get you started right away. Put it in your Gemfile and run bundle install. In your user table, ensure you have fields for username, derived key, and salt.

Salt is a randomly generated set of bytes that is appended to an encrypted password. This is done to protect against rainbow table attacks. When a new user creates an account, generate a unique salt for that user. After the salt has been generated, a key can be derived with the user’s password.

The PBKDF2 model is the same as it would be in a clear text implementation.

class User < ActiveRecord::Base
		attr_accessible :email, :password, :password_confirmation
end

The controller, on the other hand, has a few differences.

require 'pbkdf2'

class UsersController < ApplicationController
		def index
		end

		def new
				@user = User.new
		end

		def create
				@user = User.new
				@user.email = params[:user][:email]
				@user.salt = make_salt
				@user.password_digest = encrypt(params[:user][:password], @user.salt)
				if @user.save
						redirect_to @user
				else
						render :new
				end
		end

		def show
				@user = User.find(params[:id])
		end

		def authenticate
				user = User.find_by_email(params[:email])
				if user && user.password_digest == encrypt(params[:password], user.salt)
						redirect_to user
				else
						render :index
				end
		end

		private

		def encrypt(clear_text, salt)
				derived_key = PBKDF2.new do |key|
						key.password = clear_text
						key.salt = salt
						key.iterations = 10000
				end
				return derived_key.hex_string
		end

		def make_salt
				return SecureRandom.hex
		end
end

The encrypt function will return a string that looks something like this:

0e276cb8e5644a324b6d855862fc236242c0767b2fcd9ee58ab915e160a0c436

Store the result in the derived key field of your user table. The generated salt should be stored in the salt field. Keep in mind that the salt isn't meant to be a secret, so storing it in the same table as the password is okay. It is, however, meant to be unique and randomly generated.

Notice the number of iterations given to the PBKDF2 block. Ten thousand iterations is the current suggested guideline but understand that this can be set to accommodate specific performance needs. As the number of iterations goes up, the processing power required to authenticate users goes up. However, this also increases the time required for each and every password cracking attempt, which is what we want.

To authenticate the user, just run the same function with the salt stored in the table and the password given by the user. If the result matches the derived key that was stored in the table when the account was created, the user is authenticated. Now let’s look at the BCrypt implementation.

BCrypt

The beauty of BCrypt is that Rails 3 does most of the work for you. However, there a few key steps that must be done to use it. First, you need to put bcrypt-ruby in your Gemfile and bundle install it.

Next, you must make sure that a field called password_digest is in your user table with a string type. You'll also need some sort of username field as well, such as an email address. You will not, however, need a salt field. This is an advantage to BCrypt in my opinion.

The next step to utilizing Rails 3's built in BCrypt support is to put has_secure_password in your user model as illustrated below.

class User < ActiveRecord::Base
		has_secure_password

		attr_accessible :email, :password, :password_confirmation
end

You're now ready to allow users to create accounts. The UsersController would look something like this. Again, you can move the logic to a better place, this is a simple example.

class UsersController < ApplicationController
		def index
		end

		def new
				@user = User.new
		end

		def create
				@user = User.new(params[:user])
				if @user.save
						redirect_to @user
				else
						render :new
				end
		end

		def show
				@user = User.find(params[:id])
		end

		def authenticate
				user = User.find_by_email(params[:email])
				if user.try(:authenticate, params[:password])
						redirect_to user
				else
						render :index
				end
		end
end

When the user has been created, the generated password digest will look something like this:

$2a$10$VF.56hJoO/pjarIWT/xZB.cixr.Bz4B152t16WIbFhibCyDn/g9ue

BCrypt stores the salt along with the derived key together so you don't have to mess with it. The salt is taken care of for you and the digest is stored in a single field in the database. The iteration count is also stored, making this algorithm easily scalable as computational speeds increase.

Wrap Up

As you can see, implementing solid password practices is quick and easy. There is very little extra code added to the plain text version, to get the PBKDF2 or the BCrypt version. While I've focused on Ruby on Rails in these examples, implementations are available for many other languages that are ready to be used. C#, Java, and Python are just a few examples.

Databases full of usernames, emails, and passwords are being stolen at an alarming rate. BCrypt and PBKDF2 are two ways to securely store passwords and protect your users’ identities. Single hashed and salted passwords are not enough any longer. Use one of these methods and begin increasing your iteration count today.