可逆式で暗号化を実装してみた
ソース
# frozen_string_literal: true
# NOTE: 可逆式で暗号化して保存する
# 対象をcodeとしたい場合
# * テーブルに encrypted_code を定義する
# * テーブルに digested_code を定義する(indexに使用する)
# * モデルに attr_encrypted :code を定義する
# model_instance.code = xxx として使用する
module Encryptable
extend ActiveSupport::Concern
ENCRYPTE_PURPOSE = "XXXX"
ENCRYPTION_SALT = if Rails.env.staging? || Rails.env.production?
["SECRET_KEY_BASE"][1..32]
else
Rails.application.credentials.config[:secret_key_base][1..32]
end
# NOTE: 下記が定義される
# code, code=, digest_code
included do
def self.attr_encrypted(attribute) # rubocop:disable Metrics/AbcSize
encrypt_column_name = "encrypted_#{attribute}"
digest_column_name = "digested_#{attribute}"
crypt = ActiveSupport::MessageEncryptor.new(ENCRYPTION_SALT)
# NOTE: 非暗号の値のゲッター
define_method(attribute.to_s) do
encrypted_data = public_send(encrypt_column_name.to_s)
return nil if encrypted_data.blank?
decrypted_data = instance_variable_get("@#{attribute}")
return decrypted_data if decrypted_data.present?
decrypted_data = crypt.decrypt_and_verify(encrypted_data, purpose: ENCRYPTE_PURPOSE)
instance_variable_set("@#{attribute}", decrypted_data)
end
# NOTE: 非暗号の値のセッター
define_method("#{attribute}=") do |val|
encrypted_data = public_send(encrypt_column_name.to_s)
# NOTE: 値に差分がない場合なにもしない(同じ値でも暗号値が変わるため)
return if encrypted_data.present? && val == crypt.decrypt_and_verify(encrypted_data, purpose: ENCRYPTE_PURPOSE)
instance_variable_set("@#{attribute}", val)
encrypted_data = crypt.encrypt_and_sign(val, purpose: ENCRYPTE_PURPOSE)
public_send("#{encrypt_column_name}=", encrypted_data)
public_send("#{digest_column_name}=", Encryptable.generate_digest(val || ""))
end
# NOTE: 生のデータから検索を行う条件を設定
scope "digest_#{attribute}", ->(val) { where(sanitize_sql(digest_column_name) => Encryptable.generate_digest(val)) }
end
end
def self.generate_digest(val)
OpenSSL::Digest::SHA256.hexdigest(ENCRYPTION_SALT + val)
end
end
参考
-
attr-encrypted/attr_encrypted: Generates attr_accessors that encrypt and decrypt attributes iv(initialize vector)の実装の必要性に気付き素直にgemを使用することにした。 (ちなみに、桁数が大きくなるほど、暗号化した後の桁数の幅は、自前で実装した方が少ない)
-
[可逆式、不可逆式で暗号化したい ベジタブルプログラム](https://www.blog.v41.me/posts/ab5008a3-27a0-4053-8632-8b6ff84fd0fe)