Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move length-validation for encrypted columns after schema is loaded #43009

Merged
merged 1 commit into from
Aug 13, 2021

Conversation

jorgemanrubia
Copy link
Contributor

@jorgemanrubia jorgemanrubia commented Aug 12, 2021

Doing it before makes it require a database connection at class loading time, which can be a source of trouble.

For context, we validate length for encrypted columns by default because it uses compression under the hood, and this can be leveraged to store huge payloads specially crafted to be compressible.

See #42991 (comment)

cc @byroot @hahmed

Doing it before meant it required a database connection at class loading
time, which was a source a trouble.

See rails#42991 (comment)
@jorgemanrubia jorgemanrubia force-pushed the encryption-lenght-validation branch from 7d0340a to f0fe547 Compare August 12, 2021 20:53
Copy link
Member

@byroot byroot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me !

@byroot byroot merged commit 02d989e into rails:main Aug 13, 2021
@byroot
Copy link
Member

byroot commented Aug 13, 2021

Thank you!

validate_column_size attribute_name
end
end

def validate_column_size(attribute_name)
if table_exists? && limit = columns_hash[attribute_name.to_s]&.limit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly outside the diff and apologies for the nitpick comment.

I'm wondering why we call table_exists? for every attribute_name is the table for every attribute the same? Can we move this call a little higher and return early in the method add_length_validation_for_encrypted_columns?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(found this whilst reading the code, it's totally unrelated to the changes so I'm not sure if it's worth doing) I think we can move this https://github.com/rails/rails/pull/43009/files#R51 slightly higher too, because the scheme looks to be the same for every attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why we call table_exists? for every attribute_name is the table for every attribute the same?

Great catch @hahmed. With the new approach where we only add validations when the schema loads, so this check is not needed. I had added it back in the day to deal with the situation where Rails tried to load a class with encrypted attributes without a table (such as when you don't use action text but Rails loads it). I removed the check 👍.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move this https://github.com/rails/rails/pull/43009/files#R51 slightly higher too, because the scheme looks to be the same for every attribute.

@hahmed sorry which line is that? I can't see which piece of code you are referring to in that link 😅.

Thanks for the review!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, I meant this line here https://github.com/basecamp/rails/blob/f0fe547e20476e00637d15de57c14532fec6c01c/activerecord/lib/active_record/encryption/encryptable_record.rb#L51

So I think that we could do something like this?

def encrypts(*names, key_provider: nil, key: nil, deterministic: false, downcase: false, ignore_case: false, previous: [], **context_properties)
          self.encrypted_attributes ||= Set.new # not using :default because the instance would be shared across classes
          scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, downcase: downcase, \
              ignore_case: ignore_case, previous: previous, **context_properties

          names.each do |name|
            encrypt_attribute name, scheme
          end
        end

As in, we don't need to compute the scheme for every attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh totally @hahmed, good eye on that one 🙏! I just pushed the suggested change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a new PR with the two suggestions #43021

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(added you as coauthor there)

@hahmed
Copy link
Contributor

hahmed commented Aug 14, 2021

Thanks for taking care of this @jorgemanrubia 👍

jorgemanrubia added a commit to basecamp/rails that referenced this pull request Aug 15, 2021
jorgemanrubia added a commit to basecamp/rails that referenced this pull request Aug 15, 2021
See rails#43009 (comment)

Remove `table_exists?` check that became unnecessary after rails#43009

Co-authored-by: Haroon Ahmed <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants