Open keshavbiswa opened 8 months ago
This is not related to encryption, but rather parameter filtering, for example a new rails app ssn
is part of the param filtering by default:
> Rails.application.config.filter_parameters
=> [:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn]
Create a model with ssn prefix/suffix, the output is:
#<Staff:0x0000000109c79f48
id: nil,
first_name: "tim",
ssn_name: "[FILTERED]",
ssn: "[FILTERED]",
other_ssn_name: "[FILTERED]",
name_ssn: "[FILTERED]",
created_at: nil,
updated_at: nil>
Did a little bit of debugging and it turns out something expected but really shouldn't be:
params = ActionController::Parameters.new(first_name: "Hello", other_first_name: "Hello", last_name: "World")
param_filter = ActiveSupport::ParameterFilter.new([:first_name])
=> #<ActiveSupport::ParameterFilter:0x0000000107ae67a0 @blocks=nil, @deep_regexps=nil, @mask="[FILTERED]", @no_filters=false, @regexps=[/first_name/i]>
# #filter_param is filtering both first_name and other_first_name
param_filter.filter_param("first_name", "Value")
=> "[FILTERED]"
param_filter.filter_param("other_first_name", "Value")
=> "[FILTERED]"
param_filter.filter_param("last_name", "Value")
=> "Value"
# #filter is filtering both first_name and other_first_name
param_filter.filter(params)
=> #<ActionController::Parameters {"first_name"=>"[FILTERED]", "other_first_name"=>"[FILTERED]", "last_name"=>"World"} permitted: false>
The reason why is simple:
/first_name/i.match?("first_name")
=> true
/first_name/i.match?("other_first_name")
=> true
=> [:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn]
I mean that's fine from ParamsFiltering concept and for explicitly provided filter_parameters
values. But if we're encrypting attributes, we always would want exact text matches.
I'd suggest we create filter_exact_param
method which can be used instead of filter_param
that does exact text matching for encrypted attributes.
I am also experiencing this with :email and :email_is_confirmed which is not in the ParamsFiltering
I mean that's fine from ParamsFiltering concept and for explicitly provided filter_parameters values. But if we're encrypting attributes, we always would want exact text matches.
I just got bitten by the same problem. I agree that it's surprising regarding encryption. Especially since there's an option called excluded_from_filter_parameters
that allows excluding the encrypted attribute (which also excludes the partial matching attribute), but it doesn't work for excluding the partial matching attribute on its own.
Can anyone on the rails team provide any insights here?
@keshavbiswa I think this is due ignore_case: true
option. This option allow you to have a non searchable column where you can save the value for the main column in case sensitive, and the you can read this new one when you search in the main column that is downcased.
A little bit explain is in this blog in the Case Sensitive Search part: https://hint.io/blog/Active-Record-Encryption
They use this option to see what is the backup column.
Steps to reproduce
Create a model with similar named attributes
Encrypt one attribute
Initialize the model with both attributes
This outputs
=> #<User:0x000000010c5ea648 id: nil, first_name: "[FILTERED]", last_name: "World", email: nil, other_first_name: "[FILTERED]", created_at: nil, updated_at: nil>
Encrypted attributes only has :first_name in the set
User.encrypted_attributes => <Set: {:first_name}>