Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby on Rails: Active Record Encryptionを使って属性値を暗号化するメモ

Ruby on Rails 7から導入されたActive Record Encryptionを使ってみたところ大分良さそうだったので使い方とかをメモしてきます🗒

Active Record Encryptionとは?

Active Record Encryptionは、Rails 7から導入されたActive Recordの新機能です。

特定の属性値をシンプルなDSLで暗号化して扱うことができます。

guides.rubyonrails.org

Active Record Encryptionを使ってみる

Active Record Encryptionの初期設定

Active Record Encryptionで暗号化を行うのに必要な秘密情報を以下のコマンドで生成しcredentialsに設定します。

bin/rails app:db:encryption:init

Add this entry to the credentials of the target environment:

active_record_encryption:
  primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC
  deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY
  key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz

これだけで使う準備はOKです🙆‍♂️

Active Record Encryptionで暗号化してみる

例えばユーザー作成後にトークンを送信して存在確認したい時に以下のような実装を行なったとします。

class Account::ConfirmationToken < ApplicationRecord
  belongs_to :account
  validates :token, presence: true
  validates :expired_at, presence: true

  scope :active, ->(now: Time.current) { where("expired_at >= ?", now) }

  after_initialize do |record|
    record.token ||= SecureRandom.urlsafe_base64(32)
    record.expired_at ||= 1.day.ago
  end
end

今回の例ではトークンの有効期限は1日でありハッシュ化は行わないという判断をしていますが、 DBに存在する値が何かしらの理由により漏れた場合に不正に存在確認がされてしまう恐れがあります。

このようなリスクを軽減するために以下のようにencrypts :nameのような形式で記載するだけで暗号化して保存することができます。 ※deterministic: true決定論的暗号化を使用するフラグ値です。一意制約をDBに設定するケース等、同一の値を常に同一の暗号化結果とするためにはdeterministic: trueを設定します。

class Account::ConfirmationToken < ApplicationRecord
  belongs_to :account
  validates :token, presence: true
  validates :expired_at, presence: true

  encrypts :token, deterministic: true

  scope :active, ->(now: Time.current) { where("expired_at >= ?", now) }

  after_initialize do |record|
    record.token ||= SecureRandom.urlsafe_base64(32)
    record.expired_at ||= 1.day.ago
  end
end

以下のように${name}_before_type_castで実際にDBに保存されている値を閲覧できますが、アプリケーション内で使う分には暗号化されていることを気にせず利用することができます。

Account::ConfirmationToken.create(account: @account, token: "generated_token_1")
token = Account::ConfirmationToken.find_by(token: "generated_token_1")
token.token
# => "generated_token_1"
token.token_before_type_cast
# => "{\"p\":\"4P8OBmTH31wihhJq++M1iuU=\",\"h\":{\"iv\":\"Wox4jyGiHXpaaEXN\",\"at\":\"4FZ5dHQFKGnBJacTK0PttA==\"}}"

1行追加するだけで利用できて非常に便利ですね✨

Tips: 暗号化に必要な秘密情報をconfigで管理する

Active Record Encryptionは非常に便利なのですがcredentialsに暗号化に必要な秘密情報を格納する方式ではテスト環境等で少し扱いが困るかなと思ったのですが、 以下のようにconfigでも管理できるのでテスト環境とか開発環境ではconfingの値を使ってcredencialsは修正しなくても使えるようにも出来ます。

module Dummy
  class Application < Rails::Application
    config.active_record.encryption.primary_key = "foo"
    config.active_record.encryption.deterministic_key = "bar"
    config.active_record.encryption.key_derivation_salt = "baz"
  end
end

6 Configuration

  • primary_key: The key or lists of keys used to derive root data-encryption keys. The way they are used depends on the key provider configured. It's preferred to configure it via a credential > active_record_encryption.primary_key.
  • deterministic_key: The key or list of keys used for deterministic encryption. It's preferred to configure it via a credential active_record_encryption.deterministic_key.
  • key_derivation_salt: The salt used when deriving keys. It's preferred to configure it via a credential active_record_encryption.key_derivation_salt. Active Record Encryption — Ruby on Rails Guides

参考

techracho.bpsinc.jp