DEV Community

Cover image for Common Rookie Mistake: Mass Assignment Vulnerabilities
sheenasany
sheenasany

Posted on

Common Rookie Mistake: Mass Assignment Vulnerabilities

I know that there are many articles out there talking about mass assignment and how awesome it is, but for a new developer, we must also understand the dangers that this can bring to our database as it can open us up to vicious attacks.

Even Github in 2012 wasn't safe from this kind of attack when a user managed to find a crack in their security via a public update form and exploit it by adding their own file to Rails' github. How could this have happened?! These attacks are actually a bit more common than you think, especially in complex applications that involve customizable input values and perhaps programming errors that occur when data is needed to be stored rather quickly. Well, let's get to that and how to protect your database from attacks such as these.

What exactly is mass assignment vulnerability? It's where a user is able to modify fields in your database which opens up for manipulation that was not intended on our part. Surprisingly, this happens in a lot of multiplayer video games because developers of these games, perhaps unbeknownst to them as to this particular vulnerability, are passing state data and storing it in their database, such as when not specifying the particular input from the form and their values as a single object rather than taking in whatever the user is inputting into the field. This case is unique to the front end and this video is great at going into further detail regarding how to protect the front end as well, but let us turn our focus on how we can use Ruby on Rails to form that layer of protection..

Let's say we are a programmer creating the backend to allow our user to create a session upon signing up. You've created your form in the front end which can look like this:

<form onSubmit={handleForm}>
  <p>
    Enter your email address:
    <input type="text" name="user_email" value={email}>
  </p>
  <p>
    Enter a password:
    <input type="password" name="user_password" value={password}>
  </p>
  <input type="submit" value="Sign up">
</form>
Enter fullscreen mode Exit fullscreen mode

On the back end, we've got our corresponding controller handling when a new user is created:

def signup
  user = User.create(params[:user])
  # => User<email: "john@doe.com", password: "qwerty", is_admin: false>
end
Enter fullscreen mode Exit fullscreen mode

As you can see, our params are taking in three user values: an email, a password, and a boolean stating whether that user is an administrator or not. However, our front end is only allowing the user to enter their email and password. Then how can they possibly get to our database? Just because the input is not there to allow the user to change the is_admin status does not prevent them from creating an HTTP request with is_admin in the query string or request body. They perhaps may have been able to view the request body that displays all the detail of the user's account in their developer tools or was able to guess that the param existed with the correct key. Either way, a user can infiltrate by changing the values from false to true and allowing them access to admin level status to do some heavy damage, like being able to access other user accounts or adding value to their own account such as gems, money, or other items of note.

Luckily for us, Rails has a wonderful way of raising an error if we do not protect the params on a controller when a user is created. So it will prompt us to declare which params to PERMIT upon creation. We do this by either explicitly assigning the params that we want to be taken into account within our form and only take in those params, or by white listing those specific params with a private method created within our controller. When white listing we are using what is called strong params. The use of the private method also allows the limitations of only using that method within that class which can provide a little bit more protection.

def signup
  # Explicit assignment:

   user = User.create!(
    email: params[:email],
    password: params[:password]
  )

  # or whitelisting:

  user = User.create!(
    user_params
  )

private

def user_params
        params.permit(:email, :password)
    end
end
Enter fullscreen mode Exit fullscreen mode

In creating these strong params, if a user tries to include the is_admin key and attempt to infiltrate by changing the boolean to true when sending the HTTP request, the request will continue as normal, uninterrupted, but will limit the unpermitted params as it was not defined in our private user_params method. So long hackers!

Resources:
ropsec

Top comments (0)