patternrubyrailsMinor
Simple ActiveRecord attributes encryption in Rails 5+
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
encryptable.rb
user.rb
application_record.rb
class ApplicationRecord < ActiveRecord::Base
extend Encryptable
#...
endencryptable.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
enduser.rb
class User < ApplicationRecord
encrypt_columns :email, :social_security_number
endSolution
Some notes:
-
The recommended way to overwrite accessors is to use
-
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
-
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
endCode Snippets
def crypt
@crypt ||= begin
key = ActiveSupport::KeyGenerator.new('password').generate_key('salt')
ActiveSupport::MessageEncryptor.new(key)
end
endContext
StackExchange Code Review Q#140115, answer score: 2
Revisions (0)
No revisions yet.