rails: Upgrade to Rails 7.1 alpha breaks ActiveRecord attribute encryption (deterministic)
Steps to reproduce
In Rails 7.0:
create_table "my_models", force: :cascade do |t|
t.string "my_attribute"
end
class MyModel < ApplicationRecord
encrypts :my_attribute, deterministic: true
end
MyModel.create!(my_attribute: "my value")
model = MyModel.last
model.my_attribute == "my value" # true
Upgrade to Rails 7.1-alpha and run:
model = MyModel.last # raises exception
ActiveRecord encryption configuration has not changed in the meanwhile. I don’t know with what version of Rails the ActiveRecord encryption configuration has been created. Only deterministically encrypted attributes seem to be affected by this. We also have non-deterministically encrypted attributes and those still work after upgrading to Rails 7.1-alpha.
Expected behavior
Deterministically encrypted attribute should be decrypted properly.
Actual behavior
An error is raised while attribute is being decrypted:
/Users/someuser/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/bundler/gems/rails-403e4e5b5639/activerecord/lib/active_record/encryption/encryptor.rb:58:in `rescue in decrypt': ActiveRecord::Encryption::Errors::Decryption (ActiveRecord::Encryption::Errors::Decryption)
/Users/someuser/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/bundler/gems/rails-403e4e5b5639/activerecord/lib/active_record/encryption/cipher/aes256_gcm.rb:79:in `rescue in decrypt': ActiveRecord::Encryption::Errors::Decryption (ActiveRecord::Encryption::Errors::Decryption)
/Users/someuser/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/bundler/gems/rails-403e4e5b5639/activerecord/lib/active_record/encryption/cipher/aes256_gcm.rb:75:in `final': OpenSSL::Cipher::CipherError
System configuration
Rails version: 7.1.0-alpha
Ruby version: 3.2.1
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 1
- Comments: 39 (29 by maintainers)
Commits related to this issue
- Fix Active Record encryption not picking up encryption settings with eager-loading This deals with a problem where, when eager-loading is enabled, Active Record fails to pick up settings affecting en... — committed to basecamp/rails by jorgemanrubia a year ago
- Fix Active Record encryption not picking up encryption settings with eager-loading (#48577) This deals with a problem where, when eager-loading is enabled, Active Record fails to pick up settings aff... — committed to rails/rails by jorgemanrubia a year ago
- Fix queries for deterministically encrypted attributed for data migrated from 7.0. #48530 introduced a problem for the system that extends AR queries involving deterministically encrypted attributes.... — committed to basecamp/rails by jorgemanrubia a year ago
- Fix queries for deterministically encrypted attributed for data migrated from 7.0. (#48676) #48530 introduced a problem for the system that extends AR queries involving deterministically encrypted at... — committed to rails/rails by jorgemanrubia a year ago
- Fix queries for deterministically encrypted attributed for data migrated from 7.0. (#48676) #48530 introduced a problem for the system that extends AR queries involving deterministically encrypted at... — committed to paulreece/rails-paulreece by jorgemanrubia a year ago
I feel like
new_framework_defaults_7_1.rb
should be updated to reflect this. Going through the new framework defaults one by one, then usingconfig.load_defaults 7.1
will causesupport_sha1_for_non_deterministic_encryption = false
, no? Butconfig.load_defaults 7.0
causessupport_sha1_for_non_deterministic_encryption = true
. To me, there should be a warning to explicitly set that configuration totrue
if you have encrypted data and are coming from a prior version of Rails, otherwise your app will fail to decrypt old data when loading 7.1 defaults. Right now, there’s no instructions on setting it totrue
prior to callingconfig.load_defaults 7.1
.Having to re-encrypt data can be a large undertaking when you have hundreds of millions of records, so I feel like this needs to be more apparent when upgrading, because right now I feel like it’s glossed over and missing some important details.
Still trying to dig into this issue for https://github.com/keygen-sh/keygen-api/pull/764, so apologies if any of this is incorrect.
Edit: I now notice this is the same or similar issue reported by @ezekg and documented in #49610. Sadly I upgraded before that change was merged. I’m leaving this here for those that also missed it and need to fix their database.
I’ve run into this issue after upgrading to 7.0.
When running Rails 7.1, if I try to load a record that was created by Rails 7.0 and try to read an attribute that uses
deterministic: true
, I get aCipherError
I’m using
load_defaults(7.0)
and we’ve never had any custom encryption configuration.My problem is I didn’t notice this until deploying it and since then, new records have been created. So I can read the data on new records but not old ones. Looking at
new_framework_defaults_7.1.rb
, I didn’t interpret item 2 of the encryption options as something I’d actually have to take action on since I didn’t have anykey_generator_hash_digest_class
config, and anyway, I understood this file to be things you opt in to later on and not actions that must be taken before upgrading.I was lucky in that I only had a single column with deterministic encryption, and not being able to read the old values wasn’t an immediate emergency. I came up with the following database migration to fix the old records and I wanted to share it here for anyone else finding this issue from Google.
Step 1 is un-comment this line in
new_framework_defaults_7.1.rb
Then, fix existing data:
If you have some attributes encrypted using SHA1 and others using SHA256, you will get a
ActiveRecord::Encryption::Errors::Decryption
, irrelevant whathash_digest_class
is set to.To resolve this, set the following option in addition to
config.load_defaults 7.1
:Then re-encrypt all your models having encrypted attributes:
You can then disable/remove the above option again.
Yeah, I confirmed the issue on my side. The problem is that, before #44873, data encrypted with non-deterministic encryption was always using
SHA-1
. The reason is thatActiveSupport::KeyGenerator.hash_digest_class
is set in anafter_initialize
block in the railtie config, but encryption config was running before that, so it was effectively using the previous default SHA1. That means that we were using SHA256 for non deterministic, and SHA1 for deterministic encryption as you saw @duduribeiro.The initialization problem is being fixed in #48520, but we now have the problem of existing data encrypted with two hash digests. This will affect everyone using the default key providers in 7.0.x.
I’ll prepare a patch for this. My first idea is adding an option to support decrypting data with both digest classes, so that we offer an upgrade path where things don’t break for existing users. We have support in place to support different encryption schemes, so it shouldn’t be a big change.
@duduribeiro I’ll work off your changes here https://github.com/rails/rails/pull/48520 in a new branch, since this change should happen at the same time.
@seanabrahams maybe you went to this specific scenario https://github.com/rails/rails/issues/48204#issuecomment-1598373096
Hi @jorgemanrubia. I can confirm that the fix of https://github.com/rails/rails/pull/48577 fixes our problem! Thanks
Hello, this happened to me with non-deterministic encryption as well
In order to fix, I’ve used the migration at https://github.com/rails/rails/issues/48204#issuecomment-1779505395 with this relevant change
Hi @jorgemanrubia. I’m afraid I found another issue related to this.
Prior to Rails 7.1 alpha:
Switch to Rails 7.1 alpha:
Any ideas?
Thanks for the report @bforma. I’ll get this solved this week. For the record, this should not be necessary to add (that’s implicit when coming from 7.0 defaults):
👋 @bforma can you confirm that this repo script is correct? I am unable to reproduce what you are seeing when pointing to rails
main