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

Update ActionController::Parameters to be more secure on parameters handling #16299

Merged
merged 6 commits into from
Aug 19, 2014

Conversation

sikachu
Copy link
Member

@sikachu sikachu commented Jul 25, 2014

This is the follow-up from #14384


I've fixed the code according to the recommendation, which is to go smarter while not breaking much compatibility.

So, these are the proposed changes:

  • Calling #to_h on AC::Parameters will strip out unpermitted key. What this means is that if you call #to_h on AC::Parameters that's not permitted you'll get an empty hash. If the user wants to get the hash with all the keys, they're advised to duplicate the hash and call #permit! on it.
  • AC::Parameters now have more accessor and mutator methods defined on it. These methods are methods that the resulting object didn't have correct permitted status on it.

I've adding a relevant changelog, and also mentioning that we'll move away from inheriting AC::Params from Hash on the next major release.

/cc @jeremy, @tenderlove

@@ -1,3 +1,33 @@
* `ActionController::Parameters` will stop inheriting from `Hash` and
`HashWithIndifferentAccess` in the next major release. If you use any method
that does not available on `ActionController::Parameters` you should
Copy link
Member

Choose a reason for hiding this comment

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

s/does/is, no ?

@matthewd
Copy link
Member

If the user wants to get the hash with all the keys, they're advised to duplicate the hash and call #permit! on it

Is it worth us providing a method that does dup.permit!.to_h, with an appropriately caution-inducing name? There are presumably always going to be situations where people feel the need to do so, and I wonder if a for-purpose method might be safer, rather than risking that they use permit!.to_h, for example, instead. unsafe_hash, perhaps?

@robin850 robin850 added this to the 4.2.0 milestone Jul 26, 2014
@sikachu
Copy link
Member Author

sikachu commented Jul 26, 2014

@matthewd I was thinking about that but didn't propose with this patch. I may submit another PR for the #unsafe_hash method.

@@ -141,6 +141,26 @@ def initialize(attributes = nil)
@permitted = self.class.permit_all_parameters
end
Copy link
Member

Choose a reason for hiding this comment

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

Could pass permitted to the constructor rather than introducing the new_instance_with_inherited_permitted_status wrapper:

def initialize(attributes = nil, permitted = self.class.permit_all_parameters)
  super attributes
  @permitted = permitted
end

Copy link
Member

Choose a reason for hiding this comment

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

Nevermind. Looking at options to clean up the repeated "copy subset of myself" behavior, but adding semi-private internals to the public constructor isn't a great direction.

@rafaelfranca
Copy link
Member

People usually doesn't read CHANGELOGs or upgrade guides. They will only notice that their call Hash specific methods doesn't work anymore when upgrading to Rails 5. Also, there is still some methods that may not work as expected on the current implementation that may create security issues.

I still think we should do something with these methods that are form Hash/Enum API and not from the Parameters API. Maybe we should show a deprecation warning with a security note on it.

@sikachu
Copy link
Member Author

sikachu commented Jul 31, 2014

@jeremy the code has been updated. I removed the documentation for getting unsafe hash, and maybe introducing a method to get that based on @matthewd's comment unless you think YAGNI.


@rafaelfranca CHANGELOG and release note are there for a reason. If they don't read it, well, too bad. Well documented change will benefit those who seek to read it.

What are the methods that you have a concern in? Can I fix them to make them work as expected in this PR or in a separate PR?

For the deprecation warning, I don't think there's a good clean way to do it, unless you're knowing something that I don't know. I'd rather just make those methods that we know they're available works. Then, if we want to remove them or deprecate them for 5.0, we can do so.

@rafaelfranca
Copy link
Member

What are the methods that you have a concern in?

All bangs methods select!, except! an so on. For me we should disallow them or make them part of parameters API implementing and having test to assert their behaviour.

Can I fix them to make them work as expected in this PR or in a separate PR?

I'd prefer to do this work in the same PR.

@sikachu
Copy link
Member Author

sikachu commented Aug 18, 2014

As we discussed in Campfire, it seems like we will want to address a security concern about this as well.

I've come up with a solution to this, which is that we're going to mark the params to be unsafe again when the safe (permitted) parameters hash has been changed. People will then has to mark the params to be safe again after they mutate them, or convert them to a hash and opt-out from parameters whitelisting at their own risk.

However, this will not blocking this PR to be merged, and it will be addressed in another PR.

`ActionController::Parameters#to_h` now returns a `Hash` with
unpermitted keys removed. This change is to reflect on a security
concern where some method performed on an `ActionController::Parameters`
may yield a `Hash` object which does not maintain `permitted?` status.
If you would like to get a `Hash` with all the keys intact, duplicate
and mark it as permitted before calling `#to_h`.

    params = ActionController::Parameters.new(name: 'Senjougahara Hitagi')
    params.to_h # => {}

    unsafe_params = params.dup.permit!
    unsafe_params.to_h # => {"name"=>"Senjougahara Hitagi"}

    safe_params = params.permit(:name)
    safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}

This change is consider a stopgap as we cannot chage the code to stop
`ActionController::Parameters` to inherit from
`HashWithIndifferentAccess` in the next minor release.

Also, adding a CHANGELOG entry to mention that
`ActionController::Parameters` will not inheriting from
`HashWithIndifferentAccess` in the next major version.
This is to make sure that `permitted` status is maintained on the
resulting object.

I found these methods that needs to be redefined by looking for
`self.class.new` in the code.

* extract!
* transform_keys
* transform_values
* `each`
* `each_pair`
* `delete`
* `select!`
Ruby 1.9.3 does not implement Hash#to_h, so we can't call `super` on it.
jeremy added a commit that referenced this pull request Aug 19, 2014
Update `ActionController::Parameters` to be more secure on parameters handling
@jeremy jeremy merged commit 4d1d81d into rails:master Aug 19, 2014
@sikachu sikachu deleted the ps-safer-ac-params branch August 19, 2014 18:32
sikachu pushed a commit to sikachu/rails that referenced this pull request Dec 12, 2014
As suggested in rails#16299([1]), this method should be a new public API for
retrieving unfiltered parameters from `ActionController::Parameters`
object, given that `Parameters#to_hash` will no longer work in Rails
5.0+ as we stop inheriting `Parameters` from `Hash`.

[1]: rails#16299 (comment)
sikachu pushed a commit that referenced this pull request Dec 12, 2014
As discussed in #16299[1], this attribute is not thread safe and could
potentially create a security issue.

[1]: #16299 (comment)
sikachu pushed a commit to sikachu/rails that referenced this pull request Dec 13, 2014
As suggested in rails#16299([1]), this method should be a new public API for
retrieving unfiltered parameters from `ActionController::Parameters`
object, given that `Parameters#to_hash` will no longer work in Rails
5.0+ as we stop inheriting `Parameters` from `Hash`.

[1]: rails#16299 (comment)
sivagollapalli pushed a commit to sivagollapalli/rails that referenced this pull request Dec 29, 2014
As discussed in rails#16299[1], this attribute is not thread safe and could
potentially create a security issue.

[1]: rails#16299 (comment)
sivagollapalli pushed a commit to sivagollapalli/rails that referenced this pull request Dec 29, 2014
As suggested in rails#16299([1]), this method should be a new public API for
retrieving unfiltered parameters from `ActionController::Parameters`
object, given that `Parameters#to_hash` will no longer work in Rails
5.0+ as we stop inheriting `Parameters` from `Hash`.

[1]: rails#16299 (comment)
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.

None yet

6 participants