public
Description: An INCOMPLETE ActiveRecord plugin that encrypts/decrypts attributes transparently for secure database storage
Clone URL: git://github.com/shuber/attr_encrypted.git
Search Repo:
name age message
folder CHANGELOG Fri Jun 06 11:38:28 -0700 2008 Updated README [shuber]
folder MIT-LICENSE Sun Apr 27 21:33:47 -0700 2008 * Initial import [Sean Huber]
folder README.markdown Fri Jun 06 11:38:28 -0700 2008 Updated README [shuber]
folder Rakefile Sun Apr 27 21:33:47 -0700 2008 * Initial import [Sean Huber]
folder init.rb Fri Jun 06 11:29:32 -0700 2008 Added test helpers and initializer and converte... [shuber]
folder lib/ Fri Jun 06 11:29:32 -0700 2008 Added test helpers and initializer and converte... [shuber]
folder tasks/ Sun Apr 27 21:33:47 -0700 2008 * Initial import [Sean Huber]
folder test/ Fri Jun 06 11:29:32 -0700 2008 Added test helpers and initializer and converte... [shuber]
README.markdown

Huberry::AttrEncrypted

An INCOMPLETE ActiveRecord plugin that encrypts/decrypts attributes transparently for secure database storage. It uses the standard ruby OpenSSL library by default, but can also be configured to use custom encryption classes and algorithms.

Installation

script/plugin install git://github.com/shuber/attr_encrypted

Note

This plugin sets encryption settings at the class level which means that you can't uniquely encrypt each row in a table (like storing a custom salt for each record), however this allows you to perform query searches on encrypted fields. For example: User.find_by_email('shuber@huberry.com') would still work.

Usage

Basic

Let's suppose a users table has the following fields: encrypted_email, encrypted_credit_card, first_name, last_name. You would setup the model like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :credit_card, :key => 'some_super_secret_encryption_key'
end

You can now use the attribute accessors "email" and "credit_card" to manipulate the encrypted fields.

u = User.new
u.email # nil
u.encrypted_email # ""
u.email = 'test@test.com'
u.encrypted_email # ""
u.save
u.encrypted_email # "\x96\x8E|\x13\x16BU\xC1\x10\xBCk\xA1\x10=\x87\x85"

u = User.find_by_email('test@test.com')
u.email # "test@test.com"
u.encrypted_email # "\x96\x8E|\x13\x16BU\xC1\x10\xBCk\xA1\x10=\x87\x85"

For your convenience, a call to attr_protected is placed for each of the encrypted fields automatically.

Special attribute names

By default, the plugin assumes that the real attribute name for each attr_accessor that it creates is "encrypted_#{attr}" If this is not the case, you can specify the real attribute name in the optional :attr_names hash. Let's suppose instead of having an encrypted_email field, your table has a secret_email field. You would then set up the model like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :credit_card, :key => 'some_super_secret_encryption_key', :attr_names => { :email => :secret_email }
end

Everything should work correctly now.

u = User.new
u.email = 'test@test.com'
u.save
u.secret_email # "\x96\x8E|\x13\x16BU\xC1\x10\xBCk\xA1\x10=\x87\x85"

Unique encryption keys for each attribute

Let's suppose that you'd like to be extra safe and encrypt each of the attributes with a different encryption key. Simply set up your model like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :key => 'some_super_secret_encryption_key'
  attr_encrypted :credit_card, :key => 'some_other_super_secret_key'
end

Specifying the encryption algorithm

This plugin's default encryptor wraps the standard ruby OpenSSL library and can be configured to use the following algorithms: AES128, AES192, AES256, BF, CAST5, DES, IDEA, RC2, RC4, RC5. The BF algorithm is used by default, but if you'd like to change it, just set up your model like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :credit_card, :key => 'some_super_secret_encryption_key', :encryptor_options => { :algorithm => 'RC5' }
end

You can even specify a different encryption algorithm for each attribute like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :key => 'some_super_secret_encryption_key', :encryptor_options => { :algorithm => 'RC5' }
  attr_encrypted :credit_card, :key => 'some_other_super_secret_key', :encryptor_options => { :algorithm => 'DES' }
end

Encoding

By default, attr_encrypted will automatically base64 encode/decode the encrypted values so that it stores correctly with most database field types. To disable this behavior, simply add this option:

class User < ActiveRecord::Base
  attr_encrypted :email, :key => 'some_super_secret_encryption_key', :use_encoding => false
end

Custom encryption algorithms

You may use other encryption algorithms that are not included in the standard ruby OpenSSL library by specifying an :encryptor_class, :encrypt_method, and :decrypt_method in the attr_encrypted method call.

Lets suppose you'd like to use this custom encryption class:

class SillyEncryptor
  def self.silly_encrypt(value, key, options = {})
    (value + key).reverse
  end

  def self.silly_decrypt(value, key, options = {})
    value.reverse.gsub(/key/, '')
  end
end

To use this class with the example above, you would set up the model like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :credit_card, :key => 'some_super_secret_encryption_key', 
    :encryptor_class => SillyEncryptor, 
    :encrypt_method => :silly_encrypt, 
    :decrypt_method => :silly_decrypt
end

The plugin will pass these 3 arguments (in order) to the :encrypt_method and :decrypt_method of the :encryptor_class

value   - The string to encrypt/decrypt
key     - The encryption key
options - Any options you pass through :encryptor_options (see below)

You can specify the :options by passing :encryptor_options to the attr_encrypted method like so:

class User < ActiveRecord::Base
  attr_encrypted :email, :credit_card, :key => 'some_super_secret_encryption_key', 
    :encryptor_class => SillyEncryptor, 
    :encrypt_method => :silly_encrypt, 
    :decrypt_method => :silly_decrypt,
    :encryptor_options => { :some_custom_option => 'Some value' }
end

Finding records by an encrypted field

To retrieve records by an encrypted field, you can use the find_by_ or find_all_by_ class methods generated by this plugin. For the example above you would use:

User.find_by_email('test@test.com')
User.find_by_credit_card('1234 5678 1234 5678')

You may also pass a hash of options to it just like all the other find_by_ methods that are included in Rails.

User.find_by_email('test@test.com', :conditions => '...', :order => '...')

You can even search by a combination of fields

User.find_by_email_and_credit_card('test@test.com', '1234 5678 1234 5678')

Even a mix of encrypted/non-encrypted fields

User.find_by_email_and_first_name('shuber@huberry.com', 'sean')

Default options

You can override the default attr_encrypted options by setting values in the attr_encrypted_options hash

ActiveRecord::Base.attr_encrypted_options = {
  :key => 'some_secret_key',
  :encryptor_options = {
    :algorithm => 'DES'
  }
}

Then you can have clearer class definitions

Class User < ActiveRecord::Base
  attr_encrypted :email
end

Methods generated by this plugin

This plugin generates 8 methods for each attribute that you encrypt with it:

Model.find_by_#{attribute}(value [, options])
Model.find_all_by_#{attribute}(value [, options])

Model.encrypt_#{attribute}(value) # aliased to one of the two methods below depending on the :use_encoding option
Model.encrypt_#{attribute}_with_encoding(value)
Model.encrypt_#{attribute}_without_encoding(value)

Model.decrypt_#{attribute}(value) # aliased to one of the two methods below depending on the :use_encoding option
Model.decrypt_#{attribute}_with_encoding(value)
Model.decrypt_#{attribute}_without_encoding(value)

For the example above, the plugin would generate the following methods for the email attribute:

User.find_by_email(value [, options])
User.find_all_by_email(value [, options])

User.encrypt_email(value)
User.encrypt_email_with_encoding(value)
User.encrypt_email_without_encoding(value)

User.decrypt_email(value)
User.decrypt_email_with_encoding(value)
User.decrypt_email_without_encoding(value)

Contact

Problems, comments, and suggestions all welcome: shuber@huberry.com