はじめに:困ったこと・解決したいこと
Rails 7.0ではActive RecordのデフォルトのハッシュダイジェストアルゴリズムがSHA-1からSHA-256に変更されました。
この変更が入ると、Rails 6.1以前に生成したsigned_idはRails 7.0以降で利用できなくなります。
# Rails 6.1時代に user.signed_id で生成した署名付きid
signed_id = "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1USXpORFU9IiwiZXhwIjpudWxsLCJwdXIiOiJ1c2VyL3B1YmxpY19hdHRhY2htZW50cyJ9fQ==--8e20bed4cbb616db63a518d166ac8a5bf0266a18ff7ba55bbfd9bce9d571d6c7"
# ハッシュダイジェストアルゴリズムが変わったため、Rails 7.0環境ではRails 6.1時代の署名付きidは無効
User.find_signed(signed_id) #=> nil
一時的な回避策としてはRails 7.0でも引き続きSHA-1を使う、という方法があります。
ですが、SHA-1の使用は現在では推奨されないため(参考)、あくまで一時的な回避策でしかありません。
# 一時的な回避策としては有効だが、ずっとこのままにはできない
config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA1
この記事でやりたいことは、
- デフォルトはSHA-256とする(Rails 7.0にアップデートした後はSHA-256でsigned_idを読み書きする)
- 何かしらの理由で古いsigned_idで検索しないといけない場合のみ、SHA-1で作成したsigned_idでも
find_signed
で検索できるようにする
です。
解決策
いろいろ調べた結果、以下のようなコードを書くことでSHA-1で作成した古いsigned_idをRails 7.0でも検索できるようになりました。
config/initializers/message_verifiers_rotates.rb
Rails.application.config.after_initialize do |app|
key_generator = ActiveSupport::KeyGenerator.new(
app.secret_key_base,
iterations: 1000,
hash_digest_class: OpenSSL::Digest::SHA1
)
caching_key_generator = ActiveSupport::CachingKeyGenerator.new(key_generator)
secret = caching_key_generator.generate_key("active_record/signed_id")
# ここではUserクラスでfind_signedを使う想定
User.signed_id_verifier.rotate(secret)
end
こうすると、find_signed
メソッドは最初はSHA-256で検索し、データが見つからなければSHA-1で検索するようになります。
# まずSHA-256のsigned_idとして検索
# → 見つからなければSHA-1のsigned_idとして検索
# → それでも見つからなければnil
User.find_signed(signed_id)
参考文献