Better passwords in Ruby applications with the Pwned Passwords API
Tags: ruby, rails, passwords, security
Published: 2018-04-04 01:17:45

---
title: Better passwords in Ruby applications with the Pwned Passwords API
published: true
description: The Pwned Passwords API can tell you if a password has been seen in a data breach before. Use it in your Ruby application to encourage better passwords.
tags: ruby, rails, passwords, security
canonical_url: https://www.twilio.com/blog/2018/03/better-passwords-in-ruby-applications-pwned-passwords-api.html
cover_image: https://thepracticaldev.s3.amazonaws.com/i/zgjqbwy6w4xaz59b4jq3.png
---

At Twilio we're fans of <a href="https://www.twilio.com/authy">using a second factor to protect user accounts</a>, but that doesn't mean we've forgotten the first factor. Encouraging users to pick strong passwords is still the first line of defence for their accounts.

After spending years collecting lists of passwords from publicly available data breaches at <a href="https://haveibeenpwned.com/">HaveIBeenPwned</a>, Troy Hunt has made available <a href="https://haveibeenpwned.com/API/v2#PwnedPasswords">an API to check whether a password has been used before</a>. This post will show you how to encourage your users to use stronger passwords by checking against the pwned passwords API.

## The Pwned Passwords API

In 2017 NIST (<a href="https://en.wikipedia.org/wiki/National_Institute_of_Standards_and_Technology">National Institute of Standards and Technology</a>) as part of their <a href="https://www.nist.gov/itl/tig/projects/special-publication-800-63">digital identity guidelines</a> recommended that user passwords are checked against existing public breaches of data. The idea is that if a password has appeared in a data breach before then it is deemed compromised and should not be used. Of course, the recommendations include the <a href="https://www.twilio.com/docs/tutorials/two-factor-authentication-ruby-rails">use of two factor authentication to protect user accounts</a> too.

The <a href="https://haveibeenpwned.com/API/v2#PwnedPasswords">Pwned Passwords API</a> allows you to check whether a potential password has been exposed as part of a number of data breaches across the web. There is an online version of the API where you can <a href="https://haveibeenpwned.com/Passwords">enter a password and see if it's been used before</a>. If it has, it’ll also show how many times it appeared. The data has more than 500,000,000 unique passwords that have been used before.

While you're at it, check the main <a href="https://haveibeenpwned.com/">haveibeenpwned</a> service with your email address to see if your credentials have been in any of those data breaches. Spoiler alert, it probably has!

### The API

The Pwned Passwords API allows us to check a password against the database of passwords. With the results, we can advise users to choose better passwords when they sign up for a service, when they log in or when they change their password.

Your security senses might be tingling at the prospect of sending all your users' passwords to a third-party. Thankfully you needn't worry.

Instead of sending the whole password, you only need to hash the password using <a href="https://en.wikipedia.org/wiki/SHA-1">SHA-</a><a href="https://en.wikipedia.org/wiki/SHA-1">1</a> and send the first 5 characters of the result. This returns all the hashes that are in the data set beginning with those 5 characters and if the remained of the hash is present, the password was in the list. <a href="https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/">You can read more about this technique, the dump of passwords and the API in this article</a>.

Let's take a look at how to use this in a Ruby application using a couple of gems that abstract that process away for you.

## Pwned Passwords in Ruby

If you want to use the Pwned Passwords API in any Ruby application then do I have the gem for you. It's called <a href="https://rubygems.org/gems/pwned">pwned</a> and it makes checking passwords against the API really easy. 
You can check out all <a href="https://github.com/philnash/pwned">the documentation for pwned on GitHub</a>, but here's how you get started.

Install the gem:

```bash
gem install pwned
```

Open up an irb session to test it out.

```bash
irb -r pwned
> password = Pwned::Password.new("password")
> password.pwned?
#=> true
> password.pwned_count
#=> 3303003
```

Well, how about that. The password "password" has been seen <em>more than 3 million times</em> in public data breaches. 😱

There are also shortcut methods you can use:

```bash
> Pwned.pwned?("password1")
#=> true
> Pwned.pwned_count("password1")
#=> 2310111
```

Wow. "password1" is almost as bad! It's fun to play with the data like this, but what if you want to use this in a real application? You can use the gem directly, but if you're using Rails you're in for a treat.

## Pwned Passwords and Rails

We looked at <a href="https://www.twilio.com/blog/2017/06/validate-ruby-objects-with-active-model-validations.html">how to write ActiveModel validators</a> on this blog before. The <a href="https://github.com/philnash/pwned/blob/master/lib/pwned/not_pwned_validator.rb">pwned gem comes with one out of the box</a>. If you're using Rails you can use this validator by adding the gem to your `Gemfile`:

```ruby
gem "pwned", "~> 1.2.1"
```

Install the gem by running `bundle install`. Now you have access to a `:not_pwned` validator in your models. For example:

```ruby
class User < ApplicationRecord
  has_secure_password

  validates :password, not_pwned: true
end
```

Now, validating user objects against the Pwned Passwords API is as easy as:

```ruby
user = User.new(email: "<a class="c1" href="mailto:philnash@twilio.com">philnash@twilio.com</a>", password: "password!")
user.valid?
#=> false
user.errors.messages
#=> {:password=>["has previously appeared in a data breach and should not be used"]}
```

Let's try a strong passphrase instead (but not <a href="https://xkcd.com/936/">"correct horse battery staple"</a> —that appears in the breached data twice):

```ruby
user.password = "wet koalas are terrifying"
user.valid?
#=> true
```

There are other options you can use in the validator, such as setting the threshold for the number of times a password can appear in the data or what to do if the API returns an error. <a href="https://github.com/philnash/pwned#activerecord-validator">The details are in the documentation</a>.

If you are using Rails with <a href="https://github.com/plataformatec/devise">Devise</a>, there's an even easier way to use the API.

## Pwned Passwords and Devise

To use the API with Devise there is a different gem available: <a href="https://github.com/michaelbanfield/devise-pwned_password">devise-pwned_password</a>. To use it, add the gem to your `Gemfile`:

```ruby
gem "devise-pwned_password", "~> 0.1.4"
```

Run `bundle install` to install the gem. All you need to do now is add the `:pwned_password` option to the devise method for your `User` model:

```ruby
class User < ApplicationRecord
  devise :database_authenticatable, 
         :recoverable, :rememberable, :trackable, :validatable, :pwned_password
end
```

Now Devise will check against the Pwned Passwords API when your users try to sign up.

<img alt="A sign up form showing one error that reads 'Password has previously appeared in a data breach and should never be used. Please choose something harder to guess.'" src="https://twilioinc.wpengine.com/wp-content/uploads/2018/03/PKl6U6IBFObKYXVnrYvVyzDd6g1431Tx5zoNRB01EWvDocBaeYKuknJMWG4l5TVQy1bmcQJZWpGACJ8Sff2Swcs6hTYGru2xTai63F3g02OhJ1U0RIBdxB4U19zKS43xNyIERO4.png" class="aligncenter">

You can also use the Devise plugin to warn existing users about their passwords when they sign in. To do this, you need to override `after_sign_in_path_for(resource)` in your `ApplicationController`:

```ruby
def after_sign_in_path_for(resource)
  set_flash_message! :alert, :warn_pwned if resource.respond_to?(:pwned?) && resource.pwned?
  super
end
```

## Advanced options

Both the <a href="https://github.com/michaelbanfield/devise-pwned_password">Devise plugin</a> and the <a href="https://rubygems.org/gems/pwned">pwned gem</a> will mark a password as valid if the API fails in the background. With the pwned gem you can <a href="https://github.com/philnash/pwned#network-errors-handling">change this network failure behaviour</a>. You can either set the model to be invalid or run your own proc so that you can log errors.

If you don't want to mark a password as invalid if it has only been used once or twice, both gems provide a way to set a threshold. With pwned you set a threshold in the validation:

```ruby
validates :password, not_pwned: { threshold: 1 }
```

With devise-pwned_password, open `config/initializers/devise.rb` and add the following config:

```ruby
config.min_password_matches = 2
```

For more options, check the documentation for <a href="https://github.com/philnash/pwned">pwned</a> and <a href="https://github.com/michaelbanfield/devise-pwned_password">devise-pwned_password</a>.

## Safer users with safer passwords

Using the <a href="https://haveibeenpwned.com/API/v2#PwnedPasswords">Pwned Passwords API</a> you can ensure or encourage your users to use better passwords when they sign up for accounts, as they log in or when they update their password.

As I said at the start, I also recommend <a href="https://www.twilio.com/blog/2016/01/two-factor-authentication-in-rails-4-with-devise-authy-and-puppies.html">implementing 2FA in your Rails applications</a> to keep your user accounts safe.

Are you pursuing other methods to get your users to use better passwords? Do you see the Pwned Passwords API as a good tool for this? Let me know in the comments or on Twitter at <a href="http://twitter.com/philnash">@philnash</a>.

Just finally, while I wrote the initial version of the pwned gem I want to give a shout out to <a href="https://github.com/kpumuk">Dmytro Shteflyuk</a> who contributed a number of improvements, including the ActiveModel validator. Thanks!

___

_[Better passwords in Ruby applications with the Pwned Passwords API](https://www.twilio.com/blog/2018/03/better-passwords-in-ruby-applications-pwned-passwords-api.html) was originally published on the [Twilio blog](https://www.twilio.com/blog) on March 20, 2018._