はじめに
Rails6.0からRails7.0(正確には7.0.3)にアップグレードするにあたり、データの暗号化処理をを attr_encrypted
からRails7の標準機能に置き換えた話を以下にまとめます。
Rails7にアップブレードする際に発生した問題点
アップグレードの際に以下の諸問題が発生しました。
graphqlのpreloader
https://michiomochi.com/blog/rails-v6047-to-v7023
sessionが見つからない問題
https://ta-watanabe.hatenablog.com/entry/2022/03/14/140128
https://blog.dnpp.org/api_only_rails7_with_devise
Enumerable.sumがRails7から非推奨
https://www.bigbinary.com/blog/rails-7-deprecates-enumerable-sum-and-array-sum
graphql-fragment-cacheが最新だと動かない問題
https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/
strip_tagsの仕様変更に伴う修正
https://techracho.bpsinc.jp/hachi8833/2022_06_06/118470#1-1
stylesheetのmedia属性
https://qiita.com/SoarTec-lab/items/9832bb89402e452b20bb#railsapplicationconfigaction_viewapply_stylesheet_media_default--false
これらは大体対処法がありましたが、一点非常に難儀したのは attr_encrypted
の内容とRails7の標準機能がバッティングしたことです。
attr_ecnryptedとは
要約すると、指定したカラムに対してデータを入力する際に、自動的に暗号化して保存、取り出すときは暗号をデコードしてくれます。
例として、user
モデルに email
の情報を暗号化して保存するとします。
userモデルには以下のように定義します。
t.string "encrypted_email", null: true
user.rbには
attr_encrypted :email, key: your_encrypted_key, algorithm: 'aes-256-cbc', mode: :single_iv_and_salt
を設定します。(オプション等はマニュアル参照ですが、既に非推奨)
上記によって、user.email
を参照すると、実態は user.encrypted_email
にある暗号化されたデータを透過的に扱うことが出来ます。
Rails7での標準暗号化機能
Railsはver7から attr_encrypted
で提供している物と大体同等のカラム暗号化機能を提供するようになりました。問題は attr_encrypted
で提供しているメソッドと同名のメソッドが含まれるようになり、Rails7上で attr_encrypted
が提供するメソッドを実行するとエラーが発生するようになります。
そこで、暫定的に以下のブランチに切り替えます。
https://github.com/InvestIMBY/attr_encrypted/tree/rails-7-0-support
gem "attr_encrypted", github: 'InvestIMBY/attr_encrypted', branch: 'ruby-3-rails-7-0-support'
また、今まで encrypted
というメソッドで暗号化していた物がRails7の標準機能とバッティングしてしまったため、以下のようにリネームします。
- encrypted_email = User.encrypt(:email, email)
+ encrypted_email = User.attr_encrypted_encrypt(:email, email)
元々 attr_encrypted
自体更新が停滞していたこともあり、この機会にRails7の標準暗号化機能に移行することにしました。
移行を行った事例について調べてみると、1件該当しました。
https://pagertree.com/blog/migrate-attr_encrypted-to-rails-7-active-record-encrypts
概ねこの内容に従いますが、こちらの環境ではスキーマ管理を Ridgepole
で行っているので、細かい点では読み替えが必要です。
移行手順
以下の3つの段階に分けて行います。
- 既存の encrypted_email の設定を oldemail のメソッドで取得するように変更
- 標準encrypt用のemailカラムの email を追加
- バッチで oldemail から email にデータコピー
3.で行うバッチファイルの作成とテストまでは事前に行っておきます。
暗号化キーの作成
bin/rails db:encryption:init
によって以下の様なキーが出力されます
active_record_encryption:
primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC
deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY
key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
EDITOR=vim bin/rails credentials:edit
などでcredentialファイルを編集して追記しておきます。
新暗号化用カラム
email :text(65535) not null #今回追加した物
encrypted_email :string(255)
今までは encrypted_email
によって透過的に email
を扱っていましたが、新暗号化方式からそのまま email
カラムを扱います。
同時に旧暗号化方式を以下のように修正します。
- attr_encrypted :email, key: your_attr_encrypted_key, algorithm: 'aes-256-cbc', mode: :single_iv_and_salt
+ attr_encrypted :oldemail, key: your_attr_encrypted_key, algorithm: 'aes-256-cbc', mode: :single_iv_and_salt, attribute: 'encrypted_email'
+ encrypts :email, deterministic: true
上記の設定により、user.oldemail
が旧暗号化方式を指すようになります。
データ移行バッチ
抜粋すると以下のような形になります。
User.all.find_each do |user|
if user.email.blank?
user.email = user.oldemail
user.save!
end
end
サービスをメンテインの後に、暗号化処理を新方式に切り替えたコードをマージし、バッチを実行します。
emailのデータが問題なく移行済みになっていることを確認し、 attr_encrypted
gemと encrypted_email
カラムを削除します。