July 6, 2016
This blog is part of our Rails 5 series.
In older Rails version (< 3.2), when an empty array was passed to a where
clause or to a find_by
query, it generated SQL with an IS NULL
clause.
User.find_by_email([]).to_sql
#=> "SELECT * FROM users WHERE email IS NULL"
User.find_by_email([nil]).to_sql
#=> "SELECT * FROM users WHERE email IS NULL"
Also, when JSON data of the request was parsed and params
got generated the
deep munging converted empty arrays to nil
.
For example, When the following JSON data is posted to a Rails controller
{"property_grouping":{"project_id":289,"name":"test group2","property_ids":[]}}
It gets converted into the following params in the controller.
{"property_grouping"=>{"project_id"=>289, "name"=>"test group2", "property_ids"=>nil, },
"action"=>"...", "controller"=>"...", "format"=>"json"}
This in combination with the fact that Active Record constructs IS NULL
query
when blank array is passed became one of the security threats and
one of the most complained issues in Rails.
The security threat we had was that it was possible for an attacker to issue unexpected database queries with "IS NULL" where clauses. Though there was no threat of an insert being carried out, there could be scope for firing queries that would check for NULL even if it wasn't intended.
In later version of Rails(> 3.2), we had a different way of handling blank
arrays in Active Record find_by
and where
clauses.
User.find_by_email([]).to_sql
#=> "SELECT "users".* FROM "users" WHERE 1=0 LIMIT 1"
User.find_by_email([nil]).to_sql
#=> "SELECT * FROM users WHERE email IS NULL"
As you can see a conditional for empty array doesn't trigger IS NULL
query,
which solved part of the problem.
We still had conversion of empty array to nil
in the deep munging in place and
hence there was still a threat of undesired behavior when request contained
empty array.
One way to handle it was to add before_action
hooks to the action that could
modify the value to empty array if it were nil
.
In Rails 5,
empty array does not get converted to nil
in deep munging. With this change, the empty array will persist as is from
request to the params
in the controller.
If this blog was helpful, check out our full blog archive.