After quite a bit of searching I couldn’t find any decent write-ups on an easy way to do encrypted field in Ruby on Rails. Now that I’ve got an easy solution I wanted to write it up for others. I am using symmetric key encryption (AES 256) so you wouldn’t use this solution for passwords, you should hash those instead. After you set this up the encryption/decryption is transparent and you can use your variables as if they were plain text. The database attribute is call ssn_crypted, but in the model I use the virtual attribute ssn (which is why attr_accessor is necessary). This was tested with Rails 2.2.2
First the encryption library
lib/encryption.rb
module Encryption
#Basic AES symmetric encryption functions
#based on http://snippets.dzone.com/posts/show/4975
def self.encrypt(text, key)
aes(:encrypt, text, key)
end
def self.decrypt(text, key)
aes(:decrypt, text, key)
end
private
def self.aes(m,t,k)
(aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k)
aes.update(t) << aes.final
end
end
Next the model:
app/models/person.rb
class Person < ActiveRecord::Base
attr_accessor :ssn
#virtual attribute to handle encryption behind the scenes
#The db attribute type is string so I need to convert it to Base64
def ssn=(ssn)
if not ssn.blank?
self.ssn_crypted = Base64.encode64(Encryption.encrypt(ssn,"mySuperSecretKeyIsI♥Erin"))
end
end
def ssn
if self.ssn_crypted.nil?
nil
else
Encryption.decrypt(Base64.decode64(self.ssn_crypted),"mySuperSecretKeyIsI♥Erin")
end
end
With that in place I can do things like perform validattion on ssn in the model and manipulate it in the view and/or the controller treating it as plaintext yet it lives in the db encrypted.
sqlite> select ssn_crypted from people;
4NoPMAD2CXZ0b9rBNQkPcw==
Obviously you need to guard your supersecretkey. There you have it…its cake (just not the php kind).