Continuing on with building a simple and restful user authentication system is allowing your users to recover their accounts. Account recovery is necessary as user’s have a knack for forgetting their passwords. Often, I come across account recovery methods that are crufty or insecure. Such does not need to be the case, especially with Ruby on Rails.
In this tutorial, you will use symmetric encryption to make secure forgotten password links that stop functioning after use. You will also use ActionMailer to email the link to the user’s address of record.
Setting up OpenSSL and AES
You will be using Ruby’s OpenSSL library to encrypt and decrypt forgotten password links with AES (Advanced Encryption Standard). OpenSSL has everything you need to build SSL (Secure Sockets Layer) and TLS (Transport Layer Security) functionality directly into your Ruby application. However, for this tutorial you will be focusing on OpenSSL’s set of symmetric encryption mechanisms.
To get started, require the OpenSSL and SHA2 digest libraries. Next, open a new module called Crypto and set the KEY constant to something you would consider a secret. Keep in mind, if this key gets out into the open, you leave your application open to major attacks, so keep it hush hush.
Next, make a private start module method that will prep AES in in the same way for encryption and decryption and place it at the end of your module. Specifically, you will be using 256 bit AES in ECB (Electronic Code Book) mode. Although there are more secure ways to run AES, ECB is more then sufficient for small strings and very easy to setup. Also, run your key through a 256 bit SHA2 digest to ensure it is 256 bits in length.
What are block cipher modes?
ECB is one of many different ways to run AES, however another common (and more secure) way is CBC (Cipher Block Chaining). AES in ECB mode will first break your plain text into a group of 128 bit blocks, encrypt each block and concatenate them in order to make a ciphered message. This is good for small strings (especially strings that fit within one block), but not so much when sending larger messages.
AES in CBC mode will exclusively-or (X-OR) each plain text block with the previous cipher text block before encryption. Doing so makes each every block of cipher text rely on the previous, and as such, makes cryptanalysis much more difficult. However, starting the process requires a block of text to X-OR the initial block with. This initial block is called an initialization vector (IV) and should be set and safeguarded in similar fashion as your secret key.
Now that your crypto is setup, build an encryption module method. The workings are simple. First, start your crypto in encryption mode. Next, pile your plain text string into the your crypto’s update method and finish the cipher text output. The final step is to encode the string in hexadecimal, so it can be sent in a url.
The reverse is simple too, just convert the hexadecimal string back into characters and decrypt it. Save this file in your project’s /lib directory and you are all set.
The next step is to migrate your database to include an email address for your users. Just build your migration as below and run rake db:migrate.
The Person Model
Now that your database is migrated, it is time to ensure the email addresses given by your users are valid. To do this, you will match every email address with a regular expression.
Next step is to add a few customized routes as you will be going beyond basic CRUD actions with account recovery.
The People Controller
Your recover action will search for a user with the name given by a POST request. If the user exists, send them an email with a recovery key embedded link. The key has two parts, the user’s id and their salt. Passing the salt has two purposes. First, as the salt changes whenever the user updates their password, the link will become ineffective after it has served its purpose. Second, It provides extra security if an attacker were to get your application’s source code or encryption key as the salt comes from the database. Finally, pass the recovery key, user’s email address and the server’s host name to the mail delivery method so you can construct a complete email.
NOTE: request.env[‘HTTP_HOST’] may not function properly when using mongrel behind a load balancer. If this is the case, you will have to hardcode the domain into your mail delivery method.
The Sessions Controller
First off, decrypt the key and split it into the user’s id and salt. Next, find the user’s record in the people table and confirm that their salt matches. If so, log the user in and redirect them to the edit account page with a friendly reminder to change their password.
ActionMailer methods include the basic properties of any email (sender, recipient, subject and body), however also let you pass variables into the body text. Build yours as below.
Next step is to setup your ActionMailer settings in envrionment.rb. At the end of your Rails::Initializer.run block, start a new block called ActionMailer::Base.smtp_settings. Then, configure the account you will use to send forgotten password links.
Also, remember to configure Rails to give delivery errors while you are fiddling with your mail server settings. By default, Rails is set to be silent about any problems.
Getting your views ready is a three step process. First, add the “Help” link to your layout.
Next, create your Help view.
And finally, your mailer view.
Your application now has a simple and very secure way to handle forgotten passwords. You also broke into Ruby’s OpenSSL library and are prepped to delve deeper into using proven cryptographic mechanisms to secure your application from malicious use. Finally, you also also created customized RESTful routes, thus adding functionality without building a new model.