0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rails5]カラムを暗号化しつつ、検索したい

Posted at

DBに保存する値を暗号化しつつ
検索(完全一致)もする実装をしたので、その時のメモです。

動作環境

rails 5.2.2.1

概要

Humanモデルのencrypted_nameカラムを暗号化します。
AttributeEncryptモジュール作成、Modelでincludeすることで
暗号化したいカラムを定義できます。

migrateion_file
class CreateHumen < ActiveRecord::Migration[5.2]
  def change
    create_table :humen do |t|
      t.string :encrypted_name
    end
  end
end

app/model/human.rb
class Human < ApplicationRecord
  include AttributeEncrypt
  encrypt_attribute_accessor :encrypted_name
end

encrypt_attribute_accessorで指定したカラムのセッター、ゲッター
検索用のメソッドを定義してます。

app/model/concerns/attribute_encrypt.rb
module AttributeEncrypt
  extend ActiveSupport::Concern
  class_methods do
    IV = ENV['ENCRYPTION_IV']
    SECURE = ENV['ENCRYPTION_KEY']
    def encrypt_attribute_accessor(*attr_names)
      attr_names.each do |attr_name|
        define_method(attr_name.to_s) do
          value = self[attr_name]
          value.present? ? self.class.decrypt(value) : value
        end

        define_method("#{attr_name}=") do |val|
          self[attr_name] = self.class.encrypt(val) if val.present?
        end

        singleton_class.send(:define_method, "encrypt_find_by_#{attr_name.to_s}",lambda { |val|
          val.present? ? where("#{attr_name} = '#{self.encrypt(val)}'") : self.all
        })
      end
    end

    # 暗号化
    def encrypt(password)
      crypt = StaticIVEncryptor.new(IV, SECURE, cipher: 'aes-256-cbc')
      crypt.encrypt_and_sign(password)
    end

    # 復号化
    def decrypt(password)
      crypt = StaticIVEncryptor.new(IV, SECURE, cipher: 'aes-256-cbc')
      crypt.decrypt_and_verify(password)
    end
  end

  class StaticIVEncryptor < ::ActiveSupport::MessageEncryptor
    def initialize(iv, secret, *signature_key_or_options)
      @iv = iv
      super(secret, *signature_key_or_options)
    end

    private
      # override
      # https://github.com/rails/rails/blob/32cfb82922bfc905b14d7d025287dac0b85b1668/activesupport/lib/active_support/message_encryptor.rb#L166
      def _encrypt(value, **metadata_options)
        cipher = new_cipher
        cipher.encrypt
        cipher.key = @secret

        # Rely on OpenSSL for the initialization vector
        cipher.iv = @iv
        iv = @iv
        cipher.auth_data = "" if aead_mode?

        encrypted_data = cipher.update(::ActiveSupport::Messages::Metadata.wrap(@serializer.dump(value), **metadata_options))
        encrypted_data << cipher.final

        blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
        blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
        blob
      end
  end
end

使い方

# 保存
h = Human.find(1)
h.encrypted_name = 'なまえ' # 暗号化しつつ、保存されます
h.save
h[:encrypted_name] # dbに保存されている値
# => "U3VEY0ZTajRYeXRVNVljQU5lWklCa2J3cGRnc3NwVlFrUlpROU5SUTBndz0tLVFuUXlNbTk0YkdKc1JuUnFVM2RIYlE9PQ==--93f45e44659d00d09e9596f1a9c9dd962126644c"

# 表示
h.encrypted_name # 値は復号して取り出せます
# => "なまえ"

# 検索
# encrypt_find_by_カラム名 のメソッドが追加されるのでそれを使う
Human.encrypt_find_by_encrypted_name('なまえ')
# Human Load (0.5ms)  SELECT `humen`.* FROM `humen` WHERE (encrypted_name = 'U3VEY0ZTajRYeXRVNVljQU5lWklCa2J3cGRnc3NwVlFrUlpROU5SUTBndz0tLVFuUXlNbTk0YkdKc1JuUnFVM2RIYlE9PQ==--93f45e44659d00d09e9596f1a9c9dd962126644c')

# 注意
# whereとか使っても自動で反映されない
Human.where(encrypted_name: 'なまえ')
#  Human Load (1.1ms)  SELECT `humen`.* FROM `humen` WHERE `humen`.`encrypted_name` = 'なまえ'
# => []

# whereで使うのであれば下記のように
encrypted_val = Human.encrypt('なまえ')
Human.where(encrypted_name: encrypted_val)

参考
https://qiita.com/shgkt/items/1b5a05b0a8b1960889d1

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?