Project

General

Profile

« Previous | Next » 

Revision 08f6e65b

Added by Joseph Magen almost 11 years ago

fixes #2424 - add Encryptable module and encryption_key generation

View differences:

.gitignore
config/email.yaml
config/database.yml
config/initializers/local_secret_token.rb
config/initializers/encryption_key.rb
Gemfile.lock
bundler.d/Gemfile.local.rb
*.sw?
app/models/concerns/encryptable.rb
module Encryptable
extend ActiveSupport::Concern
include EncryptionKey if defined?(EncryptionKey)
# Set encryption key for tests only if case it's not set
ENCRYPTION_KEY = '25d224dd383e92a7e0c82b8bf7c985e815f34cf5' if Rails.env.test?
if const_defined?(:ENCRYPTION_KEY)
included do
ENCRYPTION_PREFIX = "encrypted-"
before_save :encrypt_setters
end
def encrypt_setters
self.encryptable_fields.each do |field|
if send("#{field}_changed?")
self.send("#{field}=", encrypt_field(read_attribute(field.to_sym)))
end
end
end
def matches_prefix?(str)
ENCRYPTION_PREFIX == str[0..(ENCRYPTION_PREFIX.length - 1)]
end
def puts_and_logs(msg)
logger.info msg
puts msg if defined?(Rake)
end
def is_encryptable?(str)
return true unless matches_prefix?(str)
puts_and_logs "String starts with the prefix '#{ENCRYPTION_PREFIX}', so #{self.class.name} #{name} was not encrypted again"
false
end
def is_decryptable?(str)
return true if matches_prefix?(str)
puts_and_logs "String does not start with the prefix '#{ENCRYPTION_PREFIX}', so #{self.class.name} #{name} was not decrypted"
false
end
def encrypt_field(str)
return str unless is_encryptable?(str)
encryptor = ActiveSupport::MessageEncryptor.new(ENCRYPTION_KEY)
begin
# add prefix to encrypted string
str = "#{ENCRYPTION_PREFIX}#{encryptor.encrypt_and_sign(str)}"
puts_and_logs "Successfully encrypted field for #{self.class.name} #{name}"
rescue
puts_and_logs "WARNING: Encryption failed for string. Please check that the ENCRYPTION_KEY has not changed."
end
str
end
def decrypt_field(str)
return str unless is_decryptable?(str)
encryptor = ActiveSupport::MessageEncryptor.new(ENCRYPTION_KEY)
begin
# remove prefix before decrypting string
str_no_prefix = str.gsub(ENCRYPTION_PREFIX, "")
str = encryptor.decrypt_and_verify(str_no_prefix)
puts_and_logs "Successfully decrypted field for #{self.class.name} #{name}"
rescue ActiveSupport::MessageVerifier::InvalidSignature
puts_and_logs "WARNING: Decryption failed for string. Please check that the ENCRYPTION_KEY has not changed."
end
str
end
module ClassMethods
def encrypts(*fields)
class_attribute :encryptable_fields
self.encryptable_fields = fields.map(&:to_sym)
define_getter_in_db(fields.map(&:to_sym))
define_auto_decrypt_getter(fields.map(&:to_sym))
end
def encrypts?(field)
encryptable_fields.include?(field.to_sym)
end
def define_getter_in_db(fields)
fields.each do |field|
define_method "#{field}_in_db" do
read_attribute(field.to_sym)
end
end
end
def define_auto_decrypt_getter(fields)
fields.each do |field|
define_method field do
decrypt_field(send("#{field}_in_db".to_sym))
end
end
end
end
else
included do
logger.info "ENCRYPTION_KEY is not defined, so encryption is turned off for #{model_name}."
end
module ClassMethods
def encrypts(*fields)
end
end
end
end
config/initializers/encryption_key.rb.example
# Be sure to restart your server when you modify this file.
# Your encryption key for encrypting and decrypting database fields.
# If you change this key, all encrypted data will NOT be able to be decrypted by Foreman!
# Make sure the key is at least 32 bytes such as SecureRandom.hex(20)
# You can use `security:generate_encryption_key` to regenerate this file.
#module EncryptionKey
# ENCRYPTION_KEY = ENV['ENCRYPTION_KEY'] || 'uncomment and insert encryption key here'
#end
lib/foreman/util.rb
def secure_token
SecureRandom.base64(96).tr('+/=', '-_*')
end
# recommended to make encryption_key at least 32 bytes. Ex. SecureRandom.hex(20)
def secure_encryption_key
SecureRandom.hex(20)
end
end
end
lib/tasks/encrypt.rake
require 'foreman/util'
namespace :security do
desc 'Generate new encryption key'
task :generate_encryption_key do
include Foreman::Util
File.open(Rails.root.join('config', 'initializers', 'encryption_key.rb'), "w") do |fd|
fd.write("# Be sure to restart your server when you modify this file.
# Your encryption key for encrypting and decrypting database fields.
# If you change this key, all encrypted data will NOT be able to be decrypted by Foreman!
# Make sure the key is at least 32 bytes such as SecureRandom.hex(20)
# You can use `rake security:generate_encryption_key` to regenerate this file.
module EncryptionKey
ENCRYPTION_KEY = ENV['ENCRYPTION_KEY'] || '#{secure_encryption_key}'
end
")
puts "Encryption key generated in file config/initializers/local_encryption_key.rb"
puts "Restart the server and then run rake db:compute_resources:encrypt"
end
end
end

Also available in: Unified diff