Simple Two Way Encryption for Ruby on Rails
Saturday, November 14th, 2009There are many options to choose from when you need to store an encrypted value in a database. After trying a few, I recently settled on Sean Huber’s delightful attr_encrypted gem.
To use attr_encrypted you’ll need to first install the gem (gem install shuber-attr_encrypted --source http://gems.github.com), then either require it in your class or via config.gem. I’ll demonstrate how to use attr_encrypted with a User record that, for whatever reason, needs to store the users Social Security Number (SSN).
Starting from scratch, let’s generate a User model with columns for the users name and encrypted SSN:
script/generate model User name:string encrypted_ssn:string.
You probably noticed that I created the column encrypted_ssn rather than ssn. This is because attr_encrypted expects the attribute you want to encrypt to be titled encrypted_#{attribute}. I’ll show you how to manipulate this in a moment.
Let’s encrypt the users SSN by calling attr_encrypted in our User model:
class User < ActiveRecord::Base
require 'attr_encrypted' #if you aren't using config.gem
attr_accessible :name, :ssn
attr_encrypted :ssn, :key => "some_key_you_like"
Be sure to declare :ssn in both your mass-assignment allowance and your encrypted attributes. Otherwise, you will get the famous “can’t mass assign…” warning in your log, or if you are validating presence of any encrypted attributes you will receive an error that they are blank.
From here, feel free to proceed as normal. Save the users SSN to the database from a form using the :ssn method, not the :encrypted_ssn method (text_field_tag 'ssn'). This will save the encrypted SSN to your db. Calling @user.ssn will yield the users decrypted SSN.
A Couple Extras
Pass the :attribute option to attr_encrypted if you would like the column to be something other than encrypted_#{attribute}.
attr_encrypted :ssn, :key => "some_key", :attribute => "private_ssn"
You will probably need to make sure that the SSN is unique, yes? In this case you want to validate the encrypted value:
validates_uniqueness_of :encrypted_ssn
As long as you are encrypting every SSN record with the same :key this will validate properly. Cover your bases and add a unique index on encrypted_ssn in your database, also.
attr_encrypted offers quite a bit more functionality, such as custom encryptors, custom algorithms, default options and marshaling. Be sure to read through the documentation at github.com/shuber/attr_encrypted.
Finally, thanks to Sean for helping me clear up a few things in my own use. He’s quite accessible.
I'm Eric Hurst, and I make the Internet from a comfy chair in Kansas City. Send an email to "eric at erichurst dot com" (spam is bad) to hire me.
