ActiveRecord::Railtie initialization fires before ActiveSupport::Railtie
In Rails::Application::Configuration#load_defaults(7.0) it sets active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA256
Then in the ActiveSupport::Railtie it sets ActiveSupport::KeyGenerator.hash_digest_class = active_support.key_generator_hash_digest_class
However, the ActiveRecord::Railtie configures ActiveRecord::Encryption before this configuration change happens.
What this causes is the ActiveRecord::Encryption::Configurable.configure to set the context.key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key), which uses the ActiveSupport::KeyGenerator from ActiveRecord::Encryption::KeyGenerator#derive_key_from method. At this point it uses the classes default hash_digest_class of OpenSSL::Digest::SHA1.
After ActiveSupport::Railtie runs the ActiveSupport::KeyGenerator.hash_digest_class changes to OpenSSL::Digest::SHA256
I am not sure what the best fix is and there may be others, but a couple I can think of are:
ActiveRecord::Encryption is "explicit" about how it configures/uses the ActiveSupport::KeyGenerator and possibly adds additional options for hash_digest_class and iterations. This removes the dependency. It also allows the database to live past future changes to ActiveSupport defaults.
ActiveSupport::KeyGenerator changes it's class default to be OpenSSL::Digest::SHA256 and the new_framework_defaults_7_0.rb file sets it back to OpenSSL::Digest::SHA1
Steps to reproduce
- Create a new rails 7 project using all rails 7 defaults (removed
new_framework_defaults_7_0.rb file and application.rb changes to config.load_defaults 7.0)
- Configure Encryption by running
bin/rails db:encryption:init and adding the generated credentials (bin/rails credentials:edit)
Add this entry to the credentials of the target environment:
active_record_encryption:
primary_key: nYeEQD0EqOKXeRMFcUq7BX6goxGt0hPq
deterministic_key: aduCGCnZWFtRIN3xc8CiM15kZKt2Az8T
key_derivation_salt: fidtmePZxQNmR6Gc4Yv7j1lko06ISaBu
- Start a rails console (
bin/rails console)
ActiveRecord::Encryption.key_provider.encryption_key.secret
primary_key = "nYeEQD0EqOKXeRMFcUq7BX6goxGt0hPq"
key_derivation_salt = "fidtmePZxQNmR6Gc4Yv7j1lko06ISaBu"
ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key).encryption_key.secret
OpenSSL::PKCS5.pbkdf2_hmac(primary_key, key_derivation_salt, 2**16, 32, OpenSSL::Digest::SHA1.new)
OpenSSL::PKCS5.pbkdf2_hmac(primary_key, key_derivation_salt, 2**16, 32, OpenSSL::Digest::SHA256.new)
Expected behavior
ActiveRecord::Encryption.key_provider.encryption_key.secret
\xE547G\xE7\x99?\x95\xAEX\xE1\xEFS\xE9p\x87}\x93\xF8\x8A\x9Ch\x80\x95#Bee\x95\nD\x7F
Actual behavior
ActiveRecord::Encryption.key_provider.encryption_key.secret
\x8B\x17k\xA1\xC9:a\x05>N\x10\xE4\eYD\xCFUh\x10\x9Ep\x89\x10\x15vt\xE0\x94\xAAN
ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key).encryption_key.secret
\xE547G\xE7\x99?\x95\xAEX\xE1\xEFS\xE9p\x87}\x93\xF8\x8A\x9Ch\x80\x95#Bee\x95\nD\x7F
OpenSSL::PKCS5.pbkdf2_hmac(primary_key, key_derivation_salt, 2**16, 32, OpenSSL::Digest::SHA1.new)
\x8B\x17k\xA1\xC9:a\x05>N\x10\xE4\eYD\xCFUh\x10\x9Ep\x89\x10\x15vt\xE0\x94\xAAN
OpenSSL::PKCS5.pbkdf2_hmac(primary_key, key_derivation_salt, 2**16, 32, OpenSSL::Digest::SHA256.new)
\xE547G\xE7\x99?\x95\xAEX\xE1\xEFS\xE9p\x87}\x93\xF8\x8A\x9Ch\x80\x95#Bee\x95\nD\x7F
System configuration
Rails version: 7.0 (gem 'rails', github: 'rails/rails', branch: 'main')
Ruby version: ruby 2.7.2p137
ActiveRecord::Railtieinitialization fires beforeActiveSupport::RailtieIn
Rails::Application::Configuration#load_defaults(7.0)it setsactive_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA256Then in the
ActiveSupport::Railtieit setsActiveSupport::KeyGenerator.hash_digest_class = active_support.key_generator_hash_digest_classHowever, the
ActiveRecord::RailtieconfiguresActiveRecord::Encryptionbefore this configuration change happens.What this causes is the
ActiveRecord::Encryption::Configurable.configureto set thecontext.key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key), which uses theActiveSupport::KeyGeneratorfromActiveRecord::Encryption::KeyGenerator#derive_key_frommethod. At this point it uses the classes defaulthash_digest_classofOpenSSL::Digest::SHA1.After
ActiveSupport::Railtieruns theActiveSupport::KeyGenerator.hash_digest_classchanges toOpenSSL::Digest::SHA256I am not sure what the best fix is and there may be others, but a couple I can think of are:
ActiveRecord::Encryptionis "explicit" about how it configures/uses theActiveSupport::KeyGeneratorand possibly adds additional options forhash_digest_classanditerations. This removes the dependency. It also allows the database to live past future changes to ActiveSupport defaults.ActiveSupport::KeyGeneratorchanges it's class default to beOpenSSL::Digest::SHA256and thenew_framework_defaults_7_0.rbfile sets it back toOpenSSL::Digest::SHA1Steps to reproduce
new_framework_defaults_7_0.rbfile andapplication.rbchanges toconfig.load_defaults 7.0)bin/rails db:encryption:initand adding the generated credentials (bin/rails credentials:edit)bin/rails console)Expected behavior
ActiveRecord::Encryption.key_provider.encryption_key.secret\xE547G\xE7\x99?\x95\xAEX\xE1\xEFS\xE9p\x87}\x93\xF8\x8A\x9Ch\x80\x95#Bee\x95\nD\x7FActual behavior
ActiveRecord::Encryption.key_provider.encryption_key.secret\x8B\x17k\xA1\xC9:a\x05>N\x10\xE4\eYD\xCFUh\x10\x9Ep\x89\x10\x15vt\xE0\x94\xAANActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key).encryption_key.secret\xE547G\xE7\x99?\x95\xAEX\xE1\xEFS\xE9p\x87}\x93\xF8\x8A\x9Ch\x80\x95#Bee\x95\nD\x7FOpenSSL::PKCS5.pbkdf2_hmac(primary_key, key_derivation_salt, 2**16, 32, OpenSSL::Digest::SHA1.new)\x8B\x17k\xA1\xC9:a\x05>N\x10\xE4\eYD\xCFUh\x10\x9Ep\x89\x10\x15vt\xE0\x94\xAANOpenSSL::PKCS5.pbkdf2_hmac(primary_key, key_derivation_salt, 2**16, 32, OpenSSL::Digest::SHA256.new)\xE547G\xE7\x99?\x95\xAEX\xE1\xEFS\xE9p\x87}\x93\xF8\x8A\x9Ch\x80\x95#Bee\x95\nD\x7FSystem configuration
Rails version:
7.0(gem 'rails', github: 'rails/rails', branch: 'main')Ruby version:
ruby 2.7.2p137