-
Notifications
You must be signed in to change notification settings - Fork 21
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
'resource_name'_params whitelists can be bypassed (and other strong params glitches) #14
Comments
Robert, thanks for spotting these problems.Yes, these issues are intermingled quite a bit. Separately, 1/ Only the 2/ Yes, new_resource is a problem. Your changes there look good. 3/ Overriding 4/ The default behaviour of resource_params currently, return params.fetch(resource_name, {}).permit( *(resource_service.content_columns.map(&:name) - [ 'updated_at', 'created_at' ]) ) is an effort to match the params white listed in a standard scaffold controller so that an rc_rails controller can still be written with just one line of code, e.g.
but, yes, this behaviour could be insecure if not overridden by a custom I can see that calling |
First off, completely agreed that the docs need an update -- but best to have consensus on what the code ought to do first! Also agreed that NoMethodError is a better exception to throw. On overriding As to a softer approach than the error... the difference between this and scaffolding is that scaffold-generated code is visible and meant to be changed, and having it handled automatically behind the scenes adds that much more risk of out-of-sight, out-of-mind. And this generally will need to be changed before exposing a webapp to the wide internet; as the As is, with my full set of changes, a fully permissive policy is as easy as
and I'd worry about making it easier than that. (On the other hand, if there's something we can do to make "add this here" more direct in web-console, for people that use it, I'd be for that; I'm just not familiar enough with its innards to know how that would work.) |
@rst if you're already finding using Yes, the existing default behaviour white-list of params should be removed, so a (One idea I just had: A safer way to maintain the existing default white-list of params could be an option like this...
so developers must really deliberately opt into it, nothing is hidden, and you can still get an easy one-line-of-code controller if you want.) |
Updated my branch to make most of the above changes, and update the docs; will shortly open it as a PR. The one thing I didn't do is the |
It's great to see this gem updated to deal with Rails 4 and 5, but the integration with the (then-)new strong parameters machinery seems to have a few rough spots. I've got code that attempts to deal with these issues on a branch of my clone, but figured I'd discuss the issues first before making it a formal PR.
The most serious is that even when a controller defines an explicit whitelist for parameters, as "#{resource_name}_params", it can be bypassed in certain circumstances. There's actually an illustration of the problem in the test suite. The
CommentsController
in the sampleapp.rb
definescomment_params
as follows:The obvious intention here is that only
:user_id
be settable through mass assignment. But if you look atcomments_controller_spec.rb
, there'sIf the
comment_params
whitelist were being applied, you'd expect this to fail, as the whitelist has onlyuser_id
and notname
as permitted parameters. In fact, it passes. The reason for this turns out to be this code inresource_methods.rb
:What happens is that the
create
inactions.rb
invokesnew_resource(resource_params)
, which invokescomment_params
. So far, so good. However,comment_params
sees no parameters on its whitelist, and so returns an emptyActionController::Parameters
. That answers true toblank?
, which causes the code quoted above, innew_resource
, to go digging around inparams
directly, bypassing the whitelist. If this code were invokingresource_params
directly instead, to get a default set of parameters, this anomaly wouldn't occur.The behavior of
resource_params
itself has a couple of other oddities. First off, if a controller defines#{resource_name}_params
, that gets invoked -- but if the controller tries to overrideresource_params
by itself (e.g.,it doesn't work. The problem is that the controller's own definition of
resource_params
is shadowed by the one inresource_methods.rb
, which never tries to invokesuper
. This can be dealt with by adding anif defined?(super)
to invoke the controller's own definition if it exists.The last issue I've found here is that if no explicit
_params
method can be found, the defaultresource_params
inresource_methods.rb
assembles a whitelist for itself out of known column names. That mirrors the permissive default behavior of Rails 3 and earlier, in which the default policy (unless you asked for something else) was to permit mass-attribute setting increate
orupdate
to alter any attribute. However, the Rails team made a deliberate design choice to move away from that policy when they did strong parameters, because it had proven to be an unsafe default. It's still possible to get a permissive policy if you ask for one, e.g.:but in stock Rails, you need to explicitly ask for it; you don't get it out of the box.
(An example of the problems that prompted this change -- in fact, the cause for it -- was on Github itself. A whiteish-hatted security researcher who thought Rails needed stronger checks updated the
user_id
of one of his own ssh keys to transfer it to a committer to the Rails project itself, and used it to make a commit onto Rails master. His commit was benign -- it just added a text file -- but it was enough to illustrate the danger of allowing stray attributes, such asuser_id
to be set through forms that weren't expecting them.)To harmonize with this design intent, my proposed changes remove the default whitelist. The proposal is that it's still possible to get a permissive policy for some or all of your controllers, by an explicit
in a particular controller, a base class for a set of them (e.g.,
AdminController
, if all your admins have console access too, and access controls are pointless), or for quickie throwaways, inApplicationController
itself, but, as in base Rails, you should have to explicitly ask for it.As noted above, I do have code to deal with all these issues (somewhat intermingled). Most of the changes are to specs, to make sure that they try to set only parameters that are on whitelists. (For the most part, the whitelists already were there in
app.rb
, but because of thenew_resource
issue, they were being ignored in a lot of cases.) But I thought I'd raise them for discussion before opening a formal PR...The text was updated successfully, but these errors were encountered: