今回はカラムの暗号化技術をGemのLockboxからRails7で導入されたActiveRecordEncryptionへ移行する手順についてまとめました。ActiveRecordEncryptionへの移行手順がまとめられた記事が少なく、データの移行に試行錯誤したため備忘録としてまとめています。
背景
Rails7以前はカラムの暗号化機能がなく、Gemを使用することで要件を満たしていました。しかしRails7からはActiveRecord Encryptionが導入され、カラム暗号化機能をデフォルトで使うことができるようになりました。
Active Recordはアプリケーションレベルの暗号化をサポートします。これは、暗号化する属性を宣言し、必要に応じて暗号化と復号をシームレスに行うしくみです。暗号化の層は、データベース層とアプリケーション層の間に置かれます。アプリケーションがアクセスするのは暗号化されていないデータですが、データベースには暗号化されたデータが保存されます。
Lockboxとは

ActiveRecordEncryptionとは

移行手順
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用の記述
1. ActiveRecordEncryptionの導入
以下の記事を参考にActiveRecordEncryptionの導入を行います。

注意点として、モデルの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のメソッドになります。
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への移行が完了です。
移行手順まとめ
- ActiveRecordEncryptionの導入
- データを移行するrake taskを作成
- 暗号化されていないデータをサポート対象にする
- 1~3を実装したPRをマージ
- rake taskの実行
- Lockboxの削除
参考
- https://techtechmedia.com/how-to-use-lockbox/
- https://techtechmedia.com/active-record-encryption-rails7/
今回はカラムの暗号化技術をGemのLockboxからRails7で導入されたActiveRecordEncryptionへ移行する手順についてまとめました。ピンポイントの内容でしたが、どなたかのお役に立てられれば幸いです。