HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyrailsMinor

Simple ActiveRecord attributes encryption in Rails 5+

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
encryptionsimpleactiverecordattributesrails

Problem

The task at hand was to ensure that certain columns in the database are encrypted in case the database data is stolen. I have a feeling that a lot can be improved here (naming, DSL, encapsulations) while keeping the functionality as simple as it is. Let me know what you think, I'd appreciate any suggestions and ideas!

application_record.rb

class ApplicationRecord < ActiveRecord::Base
  extend Encryptable
  #...
end


encryptable.rb

module Encryptable
  def encrypt_columns(*columns)
    columns.each do |column|
      if self.column_names.include?(column.to_s)
        define_method "#{column}=" do |value|
          self[column] = self.class.crypt.encrypt_and_sign(value)
        end

        define_method "#{column}" do
          self.class.crypt.decrypt_and_verify(self[column])
        end
      end
    end
  end

  def crypt
    salt = '"\xCA)P\x9Dbc\xF7\x85B\xF3v}*p\xA6~\xBAR\xC1K\xE3\x88\xB8\x18\xF7\xD1\xB6e0\x98\xEF \xB9%\xB3\x12\x9C\xFE,\x89\xF4\xDE\xED!:7\xAE<q\xB1.<~\xA3m\e\x8C\t\xCF&/3\xCE\xAE"'
    key = ActiveSupport::KeyGenerator.new('password').generate_key(salt)

    @crypt ||= ActiveSupport::MessageEncryptor.new(key)
  end
end


user.rb

class User < ApplicationRecord
  encrypt_columns :email, :social_security_number
end

Solution

Some notes:

-
The recommended way to overwrite accessors is to use super.

-
As @Flambino says, it's better not to hardcode secrets directly in the code. It can be a file not committed to the repository.

-
I'd write the crypt method this way:

def crypt
    @crypt ||= begin
      key = ActiveSupport::KeyGenerator.new('password').generate_key('salt')    
      ActiveSupport::MessageEncryptor.new(key)
    end
  end

Code Snippets

def crypt
    @crypt ||= begin
      key = ActiveSupport::KeyGenerator.new('password').generate_key('salt')    
      ActiveSupport::MessageEncryptor.new(key)
    end
  end

Context

StackExchange Code Review Q#140115, answer score: 2

Revisions (0)

No revisions yet.