Skip to content

chaione/attribute_queryable_encrypted

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AttributeQueryableEncrypted

Assigns a digest-hashed value to an attribute writer using a portion of the value assigned to each attribute's normal writer. The digest-hashed prefix can then be used to identify other objects with the same prefix without revealing the underlying value.

AttributeQueryableEncrypted was inspried by shuber's excellent attr_encrypted gem, and aims for compatibility. It attempts to addresses a shortcoming of unsalted encryption, where encrypted columns are queryable, but attackable using a precomputed "rainbow table". By exposing only a portion of the unsalted encrypted data to precomputed attacks, AttributeQueryableEncrypted reduces the need for a full-table scan on encrypted data.

Selecting multiple candidates with matching prefix digests and subsequently decrypting the full salted/encrypted data field to find a exact match will reduce the need for a full table scan. You should use attr_encrypted, or your own crypto logic, to handle encrypting and decrypting the appropriate full data field.

Example:

class HiddenValue                                                              
  include AttributeQueryableEncrypted::PrefixAttributes                                                        
  attr_accessor :prefix_data_digest
  attribute_queryable_encrypted :data
  
  def data=(something)
    ...something fancy that obscures the data...
  end
end                                                                            
                                                                             
hider = HiddenValue.new                                                        
                                                                             
hider.data = "This is a string"                                                
hider.prefix_data_digest                                                       
  # => "a37010c994067764d86540bf479d93b4d0c3bb3955de7b61f951caf2fd0301b0"      

ActiveRecord:

ActiveRecord users gain a find_all_by_[prefix]_[attribute] query method for their prefix digest column:

  HiddenValue.find_all_by_prefix_data("This is ")
  # => [#<HiddenValue id: 1, encrypted_data: "MWE2ODg0ZTVmNTA2M2I3MTZmMWQxZGI3NzA0MjgyMzRj...", prefix_data_digest: "MTgyODBlMWFkNGZiMjAyZTc5Y2FiYTcxODZhYTg1OWM3OGNhOWI...">, #<HiddenValue id: 2, encrypted_data: "WQxZGI3NzANTA2M2I3MTZmMj0MjgyMzRMWE2ODg0ZTVm...", prefix_data_digest: "MTgyODBlMWFkNGZiMjAyZTc5Y2FiYTcxODZhYTg1OWM3OGNhOWI...">]

The returned records - a subset of the full table - can then be iterated over to find an exact match.

A convenience method is provided to do this for you - find_by_[attribute]. Note that it requires an attribute getter method (but not necessarily a database column) that provides a cleartext match for your query argument:

  HiddenValue.find_by_data("This is a string")
  # => #<HiddenValue id: 1, encrypted_data: "MWE2ODg0ZTVmNTA2M2I3MTZmMWQxZGI3NzA0MjgyMzRj...", prefix_data_digest: "MTgyODBlMWFkNGZiMjAyZTc5Y2FiYTcxODZhYTg1OWM3OGNhOWI...">

You'll need to create an appropriately-named prefix digest column on your own.

AttributeQueryableEncrypted is automatically included into ActiveRecord::Base when the gem is installed. To use, just specify the columns you want:

  class HiddenValue < ActiveRecord::Base
    attr_encrypted :data, :key => { |record| SiteKey + record.salt }
    attribute_queryable_encrypted :data, :length => "35%", :suffix => "queryable"
  end

Your :data accessor (provided here by attr_encrypted) should exist prior to defining the queryable accessor.

Options:

  • :length - an integer value length, or percentage expressed as a string ("72%"). Default is "50%".
  • :prefix - prefix name for the storage accessor. Default is "prefix"
  • :suffix - suffix name for the storage accessor. Default is "digest"
  • :encode - Base64 encode the digest hash, suitable for database persistence. Default is false.
  • :stretches - an integer number of iterations through the digest algorithm. More will reduce the ease of a precomputed attack. Default is 3.
  • :key - an optional key to salt the digest algorithm. Default is nil.
  • :digest - the Digest class to use. Must respond to #update. Default is Digest::SHA2.

If you choose to use :stretches and/or :key, you should keep their values secret.

Requirements:

  • Ruby >= 1.9
  • ActiveSupport >= 3.0
  • ActiveRecord >= 3.0 for ActiveRecord usage

Warnings

  • This technique is not without shortcomings, notably that the entire prefix digest is subject to a precomputed attack.
  • You should consider using secret values for :stretches and :key, and setting the :length option to a level that obscures an appropriate amount of your data without potentially giving away too much.
  • Increasing :stretches incurs a small performance penalty.
  • Decreasing :length can return more records in the initial matched set, potentially decreasing performance. Increasing :length makes more of the data subject to a precomputed attack.

Copyright

(The MIT License)

© 2011 (Scott Burton, ChaiOne)

About

Makes querying encrypted & salted attributes mildly less horrible.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages