Rails

【Rails】暗号化技術をLockboxからActiveRecordEncryptionへ移行する手順

Rails

 

今回はカラムの暗号化技術をGemのLockboxからRails7で導入されたActiveRecordEncryptionへ移行する手順についてまとめました。ActiveRecordEncryptionへの移行手順がまとめられた記事が少なく、データの移行に試行錯誤したため備忘録としてまとめています。

背景

Rails7以前はカラムの暗号化機能がなく、Gemを使用することで要件を満たしていました。しかしRails7からはActiveRecord Encryptionが導入され、カラム暗号化機能をデフォルトで使うことができるようになりました。

Active Recordはアプリケーションレベルの暗号化をサポートします。これは、暗号化する属性を宣言し、必要に応じて暗号化と復号をシームレスに行うしくみです。暗号化の層は、データベース層とアプリケーション層の間に置かれます。アプリケーションがアクセスするのは暗号化されていないデータですが、データベースには暗号化されたデータが保存されます。

参考: Active Record と暗号化

Lockboxとは

Rails
【Rails】秘匿情報を暗号化し、管理することができるGem「lockbox」の使い方について簡単にまとめてみる秘匿情報を暗号化した状態で管理することができるGem「lockbox」の使い方について簡単に解説しています。秘匿情報を扱う際の選択肢の1つになれば幸いです。...

ActiveRecordEncryptionとは

Rails
【Rails7】ActiveRecord Encryptionを用いてデータベースの情報を暗号化する方法メモRails7で導入されたデータベースの情報を暗号化する機能「ActiveRecord Encryption」を触ってみたので、その機能について簡単に紹介しています。これまでデータベースの情報を暗号化したい場合は、lockboxやbcrypt-rubyといった外部gemの使用が一般的でしたが、Rails7からはデフォルトでその機能が使えるようになりました。...

移行手順

0. 前提

LockboxとActiveRecordEncryptionは共存することができます。(= 両方のコードが存在する状態でも書き込み・読み込み処理ができます)

# Model

class Model < ApplicationRecord
  encrypts :password # ActiveRecordEncryption用の記述
  has_encrypted :password, migrating: true # Lockbox用の記述
end
# Schema

t.text "password" # ActiveRecordEncryption用の記述
t.text "password_ciphertext" # Lockbox用の記述
migrating: trueを追加することで、passwordカラムがmigrated_passwordカラムに変更されます。Lockbox単体使用ではmigrating: trueは必要ありませんが、LockboxとActiveRecordEncryptionを併用する場合は必要になります。

1. ActiveRecordEncryptionの導入

以下の記事を参考にActiveRecordEncryptionの導入を行います。

Rails
【Rails7】ActiveRecord Encryptionを用いてデータベースの情報を暗号化する方法メモRails7で導入されたデータベースの情報を暗号化する機能「ActiveRecord Encryption」を触ってみたので、その機能について簡単に紹介しています。これまでデータベースの情報を暗号化したい場合は、lockboxやbcrypt-rubyといった外部gemの使用が一般的でしたが、Rails7からはデフォルトでその機能が使えるようになりました。...

 

注意点として、モデルのLockboxの記述にはmigrating: trueオプションを付与しましょう。これにより、Lockbox用のカラムとActiveRecordEncryption用のカラムの両方にデータが入るようになります。

# Model

class Model < ApplicationRecord
  encrypts :password # ActiveRecordEncryption用の記述
  has_encrypted :password, migrating: true # Lockbox用の記述
end

ActiveRecordEncryptionの導入が完了すると、LockboxとActiveRecordEncryptionの記述とキーが共存している状態になります。

# Model

class Model < ApplicationRecord
  encrypts :password # ActiveRecordEncryption用の記述
  has_encrypted :password, migrating: true # Lockbox用の記述
end
# Schema

t.text "password" # ActiveRecordEncryption用の記述
t.text "password_ciphertext" # Lockbox用の記述
# credentials

lockbox:
  master_key: "mklacc72432041mfdssd2"
active_record_encryption:
  primary_key: vaebG7eRV9aPbQYP2HOUUVF1sO5OhTsJ
  deterministic_key: MvwFKH0O5apPxAWR9IEBhl8JU0dp825N
  key_derivation_salt: maHdkv5C8s2og7RgoUPebaQCjy4cGAIb

2. データを移行するrake taskを作成

Lockboxのデータを空のActiveRecordEncryption用カラムに移行するrake taskを作成します。以下はその一例です。

namespace :oneshot do
  namespace :add_unencrypted_body do
    desc 'passwordにpassword_ciphertextのデータをコピーする'
    task migrate: :environment do
      puts "(実行前)passwordが空でpassword_ciphertextに値が入っている数: #{ActiveRecord::Base.connection.select_all('SELECT COUNT(*) AS count FROM models WHERE password_ciphertext IS NOT NULL AND password IS NULL').to_a[0]['count']}"
      Model.all.each do |model|
        # MEMO: モデルにmigrating: trueを定義しているため、bodyに値が入るようになる
        model.update(password: Model.decrypt_password_ciphertext(model.password_ciphertext))
      end
      puts "(実行後)passwordが空でpassword_ciphertextに値が入っている数: #{ActiveRecord::Base.connection.select_all('SELECT COUNT(*) AS count FROM models WHERE password_ciphertext IS NOT NULL AND password IS NULL').to_a[0]['count']}"
    end
  end
end

decrypt_password_ciphertextは暗号化されたデータを復号化するLockboxのメソッドになります。

参考: ankane/lockbox Decryption

3. 暗号化されていないデータをサポート対象にする

現在の状態だと、先ほど作成したrake taskを実行してもエラーになってしまいます。

そのため以下の記事を参考に、config/application.rbに暗号化されていないデータをサポート対象にする設定を追記します。

# application.rb

# MEMO: 暗号化されていないデータのサポート
config.active_record.encryption.support_unencrypted_data = true

参考: Create user failure with Rails 7.0 and encryption email

4. 1~3を実装したPRをマージ

上記の1~3を実装したPRをマージします。

5. rake taskの実行

作成したrake taskを実行しデータを移行します。

% bundle exec rake oneshot:add_unencrypted_body:migrate

6. Lockboxの削除

データがコピーされていることが確認でき次第、LockboxのGemとカラム・記述を削除したPRを作成します。このPRがマージされることでLockboxからActiveRecordEncryptionへの移行が完了です。

移行手順まとめ

  1. ActiveRecordEncryptionの導入
  2. データを移行するrake taskを作成
  3. 暗号化されていないデータをサポート対象にする
  4. 1~3を実装したPRをマージ
  5. rake taskの実行
  6. Lockboxの削除

参考

 

今回はカラムの暗号化技術をGemのLockboxからRails7で導入されたActiveRecordEncryptionへ移行する手順についてまとめました。ピンポイントの内容でしたが、どなたかのお役に立てられれば幸いです。