Why You Need Strong Parameters in Rails
Tags: rails, ruby
Published: 2022-05-18 16:57:57

You've used the strong parameters in your Rails applications, but **did you know what problem they are solving?** I didn't. So did some reading and learned about a common security vulnerability. In this post, I will explain the Mass Assignment vulnerability and how you can use the Rails strong parameters API to address it. 

### Mass Assignment Vulnerability

**What is it?**

Mass Assignment means assigning values to multiple variable or object properties at a time. 

**Why is it bad?**

Consider the following object:

```ruby
user = {
  name: "Jason",
  company: "37signals",
  owner: true
}
```

Now let's say we want to update the company's name to "Basecamp". The typical web-application way to do this would be to receive some data, preferably from a form submission from the user, and use that data to update our user's properties. 

```ruby
params = {
  # some data
  company: "Basecamp",
  # some data
}

user.update!(params)
```

Boom! You are done. But wait, the next day, Jason comes to the office and realizes there's a new owner. He's not listed as the owner anymore of the company. 

**How did that happen?** 

The answer is the other properties in the `params` object above that I didn't list. Here's what the complete object looks like:

```ruby
params = {
  name: "Jason",
  company: "Basecamp",
  owner: false
}
```

Because the software updated all the properties of the `user` using the `params` hash in its entirety, the `owner` property was updated to `false`. As a result, Jason is not an owner anymore. 

Now, of course, this is a very simple example to illustrate the problem. Of course, the web application is not foolish enough to ask whether the owner is indeed the owner on a front-end form that anyone can submit, and thankfully, Jason is still the owner. 

But you get the idea. **It's terrible to update the properties of an object from a source that we don't trust!**

> Mass assignment vulnerability occurs when your application assigns the data from a user input to multiple objects, variables, of database fields at once. Updating the object's properties allows an attacker to modify or overwrite the existing data. 

In the comments, Jeremy gave a scary real-world example. In 2012 [GitHub was compromized](https://www.zdnet.com/home-and-office/networking/github-suspends-member-over-mass-assignment-hack/) by this vulnerability. A GitHub user used mass assignment that gave him administrator privileges to none other than the Ruby on Rails project. As GitHub co-founder, Tom Preston-Werner later said,

> "The root cause of the vulnerability was a failure to properly check incoming form parameters, a problem known as the mass-assignment vulnerability," 

**How to prevent it?**

The solution is simple. Before you update any object, filter out only the properties you want and nothing else!

```ruby
class UsersController < ActionController::Base
  def create
    User.create(user_params)
  end
  
  def update
    User.find(params[:id]).update_attributes!(user_params)
  end

  private
    def user_params
      params[:user].slice(:name, :company)
    end
end
```

This pattern was so common that Rails released a plugin as well as a gem for it, and later versions of Rails (v4 onwards) provide it out of the box. Here's how it works:

## Strong Parameters

The philosophy behind strong parameters is _"**assume unsafe until proven otherwise**"_. In simple terms, Rails marks the parameters forbidden to be used in mass assignment until you explicitly mark them as safe. 

**How do you mark parameters as safe?** 

Using the `require` and `permit` methods on the `params` hash, which is an instance of `ActionController::Parameters`. 

```ruby
params.require(:client).permit(:name, :company)
```

In the above code, we explicitly mark the `client` parameter as required using the `require` method, and only permit the `name` and `company` parameters inside the `client`. If the `client` parameter has an `admin` parameter, Rails won't allow you to use it in a mass assignment operation. 

Let's open the Rails console by running `bin/rails console` and run some experiments:

```ruby
params = ActionController::Parameters.new({
  client: {
    name: "Jason",
    company: "Basecamp",
    admin: true
  },
  user: {
    name: "David"
  }
})

=> #<ActionController::Parameters {"client"=>{"name"=>"Jason", ..}} permitted: false>
```

> Yes, you can simply create a `params` object on fly. You don't need to make a request from the browser to access it. Pretty cool, right?

Notice that the output of the above code was an object with the `permitted` property set to `false`. Now, let's mark the `client` parameter as required, only permitting the `name` and `company` parameters. 

 ```ruby
client_params = params.require(:client).permit(:name, :company)
=> #<ActionController::Parameters {"name"=>"Jason", "company"=>"Basecamp"} permitted: true>
 ```

Note that after validating the parameters and allowing only the attributes we want, the result `client_params` has the `permitted` property set to `true`. The `client_params` parameter is now safe to use in a mass-assignment operation.

To permit an entire hash of parameters, use the `permit!` method.

```ruby
params.require(:client).permit!
```

Remember that the strong parameters are only validated when you try to use them to update the active model's properties. It won't prevent you from accessing the data. 

```ruby
> params
=> #<ActionController::Parameters {"client"=>#<ActionController::Parameters {"name"=>"Jason", "company"=>"Basecamp", "admin"=>true} permitted: false>, "user"=>{"name"=>"David"}} permitted: false>

# This works
params[:user]
=> #<ActionController::Parameters {"name"=>"David"} permitted: false>
params[:user][:name]
=> "David"

# This doesn't
User.update!(params[:user])
```

### Permit All Parameters

If you want to permit all parameters by default, Rails provides the `permit_all_parameters` option, which is `false` by default. If set to `true`, it will permit all the parameters (not recommended).  

```ruby
ActionController::Parameters.new
=> #<ActionController::Parameters {} permitted: false>

ActionController::Parameters.permit_all_parameters = true
=> true

ActionController::Parameters.new
=> #<ActionController::Parameters {} permitted: true>
```

### Take Specific Action for Unpermitted Parameters

You can also control the behavior when Rails finds the unpermitted parameters. For this, set the `action_on_unpermitted_parameters` property, which can take one of three values:

- `false` to do nothing and continue as if nothing happened. 
- `:log` to log an event at the `DEBUG` level.
- `:raise` to raise an `ActionController::UnpermittedParameters` exception. 

```ruby
params = ActionController::Parameters.new(name: "DHH")

### false ###
ActionController::Parameters.action_on_unpermitted_parameters = false
params.permit(:rails)
=> #<ActionController::Parameters {} permitted: true>

### log ###
ActionController::Parameters.action_on_unpermitted_parameters = :log
params.permit(:rails)
2022-05-17 18:39:23.099411 D [56269:4120 subscriber.rb:149] Rails -- Unpermitted parameter: name
=> #<ActionController::Parameters {} permitted: true>

### raise ###
ActionController::Parameters.action_on_unpermitted_parameters = :raise
params.permit(:rails)
=> #../metal/strong_parameters.rb:1002:in `unpermitted_parameters!': found unpermitted parameter: :name (ActionController::UnpermittedParameters)
```

---

I hope you now have a better understanding of why strong parameters exist in Rails and how you can use them to build safe and secure applications. If you found this article useful, or have any questions, please let me know.