Rails has always had a nice way of sanitizing user input coming from ubiquitous forms. Up until Rails 3, the solution was to list accessible fields right in your models. Then Rails 4 came along and introduced a different solution - , allowing you to take a greater control over the sanitizing process. The following text should serve as a light introduction to the topic and also as a cookbook for solving more complex tasks.
Let’s start by taking a look at how actually work. On a superficial level, every request wraps its params in , which allows you to whitelist specific keys.
If we try to use them directly without whitelisting in mass assignment, we’ll get .
To make this work, we just need to whitelist the attributes we want in the mass assignment and Rails will be happy
We don’t have to whitelist all of the attributes though, but we’ll get a warning if we miss some of them
We can even chain them together and build up the filter, as every call to will return an instance of
Using won’t mind if the permitted attribute is missing
Which can be good, but sometimes we do want to require a specific parameter to be present. We can do that by using instead of , which will raise
There’s one thing different about , and that’s it returns the actual value of the parameter, unlike which returns the actual hash.
This becomes useful when we have nested parameters, as we almost always have when using forms, for example
You might be asking what’s the difference here, since they both return the same thing … well not exactly
This simply means that if you try to use the first case in mass assignment you’ll end up getting .
With this knowledge, we can already convert our to in most of the cases.
Now let’s transform the code using .
If you are using any form of nested attributes, be it the Rails’ or just simply sending along some nested form data, we need to tell about it, as by default only allows scalar values, no hashes or arrays.
Let’s start with the simpler of those two, arrays. If we simply use on an attribute with array value, it will behave as if we didn’t permit it at all.
To solve this we simply tell that the key is an array
Doing this with hashes is very similar, we just have to name all of the keys contained in the hash
As you can see this is different from using , since that will only return the nested hash and not the parent one.
There’s one last problem with this. We can’t easily permit nested attributes when we don’t know the exact key names. This can happen for example if you’re expecting custom JSON data which can be of any format and you’re storing it using the new Rails 4 JSON support in a PostgreSQL database.
There’s a GitHub issue for this but as mentioned in the issue comments this is by design and you can easily solve it using Ruby magic, as we showed above.
We hope this article will make it easier for you to get the hang of and its sometimes not so obvious edge cases. Leave us a comment if you have any questions, tips or a missing use case. We’ll happily add it to the article.
Mass Assignment is the name Rails gives to the act of constructing your object with a parameters hash. It is "mass assignment" in that you are assigning multiple values to attributes via a single assignment operator.
The following snippets perform mass assignment of the and attribute of the model:
In order for this to work, your model must allow mass assignments for each attribute in the hash you're passing in.
There are two situations in which this will fail:
- You have an declaration which does not include
- You have an which does include
It recently became the default that attributes had to be manually white-listed via a in order for mass assignment to succeed. Prior to this, the default was for attributes to be assignable unless they were explicitly black-listed or any other attribute was white-listed with
It is important to consider which attributes can be mass assigned because code like this is so common:
Typically this is used when the user submits a form rendered by a . In an ideal world, the hash should only contain the fields we displayed on the form. However, it is trivial easy for the user to pass additional fields in their request, so in effect you're allowing a user to set any fields on , not just the ones displayed on the form.
Failure to safely use mass assignment has led to several high profile bugs in some pretty big Rails applications, like the one that allowed somebody to inject their own public key into the list of trusted keys on a Github repository and push code directly to a repository they should not have had access to.