1 Fauzil

Mass Assignment Security Rails For Windows

Last revision (mm/dd/yy): 12/21/

Introduction

Definition

Software frameworks sometime allow developers to automatically bind HTTP request parameters into program code variables or objects to make using that framework easier on developers. This can sometimes cause harm. Attackers can sometimes use this methodology to create new parameters that the developer never intended which in turn creates or overwrites new variable or objects in program code that was not intended. This is called a mass assignment vulnerability.

Alternative Names

Depending on the language/framework in question, this vulnerability can have several alternative names

  • Mass Assignment: Ruby on Rails, NodeJS
  • Autobinding: Spring MVC, woaknb.wz.sk MVC
  • Object injection: PHP

Example

Suppose there is a form for editing a user's account information:

<form> <input name=userid type=text> <input name=password type=text> <input name=email text=text> <input type=submit> </form>

Here is the object that the form is binding to:

public class User { private String userid; private String password; private String email; private boolean isAdmin; //Getters & Setters }

Here is the controller handling the request:

@RequestMapping(value = "/addUser", method = woaknb.wz.sk) public String submit(User user) { woaknb.wz.sk(user); return "successPage"; }

Here is the typical request:

POST /addUser userid=bobbytables&password=hashedpass&email=bobby@woaknb.wz.sk

And here is the exploit:

POST /addUser userid=bobbytables&password=hashedpass&email=bobby@woaknb.wz.sk&isAdmin=true

Exploitability

This functionality becomes exploitable when:

  • Attacker can guess common sensitive fields
  • Attacker has access to source code and can review the models for sensitive fields
  • AND the object with sensitive fields has an empty constructor

Case Studies

GitHub

In , GitHub was hacked using mass assignment. A user was able to upload his public key to any organization and thus make any subsequent changes in their repositories. GitHub's Blog Post

Solutions

  • Whitelist the bindable, non-sensitive fields
  • Blacklist the non-bindable, sensitive fields
  • Use Data Transfer Objects (DTOs)

General Solutions

Data Transfer Objects (DTOs)

An architectural approach is to create Data Transfer Objects and avoid binding input directly to domain objects. Only the fields that are meant to be editable by the user are included in the DTO.

public class UserRegistrationFormDTO { private String userid; private String password; private String email; //NOTE: isAdmin field is not present //Getters & Setters }

Language & Framework Specific Solutions

Spring MVC

Whitelisting

@Controller public class UserController { @InitBinder public void initBinder(WebDataBinder binder, WebRequest request) { woaknb.wz.skowedFields(["userid","password","email"]); } }

Reference

Blacklisting

@Controller public class UserController { @InitBinder public void initBinder(WebDataBinder binder, WebRequest request) { woaknb.wz.skallowedFields(["isAdmin"]); } }

Reference

NodeJS + Mongoose

Whitelisting

var UserSchema = new woaknb.wz.sk({ userid &#;: String, password &#;: String, email &#;: String, isAdmin &#;: Boolean, }); woaknb.wz.sks = { woaknb.wz.skeateSafeFields: ['userid', 'password', 'email'] }; var User = woaknb.wz.sk('User', UserSchema); _ = require('underscore'); var user = new User(woaknb.wz.sk(woaknb.wz.sk, woaknb.wz.skeateSafeFields));

ReferenceReference

Blacklisting

var massAssign = require('mongoose-mass-assign'); var UserSchema = new woaknb.wz.sk({ userid &#;: String, password &#;: String, email &#;: String, isAdmin &#;: { type: Boolean, protect: true, default: false } }); woaknb.wz.sk(massAssign); var User = woaknb.wz.sk('User', UserSchema); /** Static method, useful for creation **/ var user = woaknb.wz.sksign(woaknb.wz.sk); /** Instance method, useful for updating **/ var user = new User; woaknb.wz.sksign(woaknb.wz.sk); /** Static massUpdate method **/ var input = { userid: 'bhelx', isAdmin: 'true' }; woaknb.wz.sk({ '_id': someId }, { $set: woaknb.wz.skdate(input) }, woaknb.wz.sk);

Reference

Ruby On Rails

Reference

Django

Reference

woaknb.wz.sk

Reference

PHP Laravel + Eloquent

Whitelisting

<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { private $userid; private $password; private $email; private $isAdmin; protected $fillable = array('userid','password','email'); }

Reference

Blacklisting

<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { private $userid; private $password; private $email; private $isAdmin; protected $guarded = array('isAdmin'); }

Reference

Grails

Reference

Play

Reference

Jackson (JSON Object Mapper)

ReferenceReference

GSON (JSON Object Mapper)

ReferenceReference

JSON-Lib (JSON Object Mapper)

Reference

Flexjson (JSON Object Mapper)

Reference

Authors and Primary Editors

References and future reading

Other Cheatsheets

Early in , a developer, named Egor Homakov, took advantage of a security hole at Github (a Rails app) to gain commit access to the Rails project.

His intent was mostly to point out a common security issue with many Rails apps that results from a feature, known as mass assignment (and did so rather loudly). In this article, we'll review what mass assignment is, how it can be a problem, and what you can do about it in your own applications.


What is Mass Assignment?

To begin, let's first take a look at what mass assignment means, and why it exists. By way of an example, imagine that we have the following class in our application:

# Assume the following fields: [:id, :first, :last, :email] class User < ActiveRecord::Base end

Mass assignment allows us to set a bunch of attributes at once:

attrs = {:first => "John", :last => "Doe", :email => "woaknb.wz.sk@woaknb.wz.sk"} user = woaknb.wz.sk(attrs) woaknb.wz.sk #=> "John" woaknb.wz.sk #=> "Doe" woaknb.wz.sk #=> "woaknb.wz.sk@woaknb.wz.sk"

Without the convenience of mass assignment, we'd have to write an assignment statement for each attribute to achieve the same result. Here's an example:

attrs = {:first => "John", :last => "Doe", :email => "woaknb.wz.sk@woaknb.wz.sk"} user = woaknb.wz.sk woaknb.wz.sk = attrs[:first] woaknb.wz.sk = attrs[:last] woaknb.wz.sk = attrs[:email] woaknb.wz.sk #=> "John" woaknb.wz.sk #=> "Doe" woaknb.wz.sk #=> "woaknb.wz.sk@woaknb.wz.sk"

Obviously, this can get tedious and painful; so we bow at the feet of laziness and say, yes yes, mass assignment is a good thing.


The (Potential) Problem With Mass Assignment

One problem with sharp tools is that you can cut yourself with them.

But wait! One problem with sharp tools is that you can cut yourself with them. Mass assignment is no exception to this rule.

Suppose now that our little imaginary application has acquired the ability to fire missiles. As we don't want the world to turn to ash, we add a boolean permission field to the model to decide who can fire missiles.

class AddCanFireMissilesFlagToUsers < ActiveRecord::Migration def change add_column :users, :can_fire_missiles, :boolean, :default => false end end

Let's also assume that we have a way for users to edit their contact information: this might be a form somewhere that is accessible to the user with text fields for the user's first name, last name, and email address.

Our friend John Doe decides to change his name and update his email account. When he submits the form, the browser will issue a request similar to the following:

PUT woaknb.wz.sk?user[first]=NewJohn&user[email]=woaknb.wz.sk@woaknb.wz.sk

The action within the might look something like:

def update user = woaknb.wz.sk(params[:id]) if woaknb.wz.sk_attributes(params[:user]) # Mass assignment! redirect_to home_path else render :edit end end

Given our example request, the hash will look similar to:

{:id => 42, :user => {:first => "NewJohn", :email => "woaknb.wz.sk@woaknb.wz.sk"} # :id - parsed by the router # :user - parsed from the incoming querystring

Now let's say that NewJohn gets a little sneaky. You don't necessarily need a browser to issue an HTTP request, so he writes a script that issues the following request:

PUT woaknb.wz.sk?user[can_fire_missiles]=true

Fields, like , , and , are quite easily guessable.

When this request hits our action, the call will see , and give NewJohn the ability to fire missiles! Woe has become us.

This is exactly how Egor Homakov gave himself commit access to the Rails project. Because Rails is so convention-heavy, fields like , , and are quite easily guessable. Further, if there aren't protections in place, you can gain access to things that you're not supposed to be able to touch.


How to Deal With Mass Assignment

So how do we protect ourselves from wanton mass assignment? How do we prevent the NewJohns of the world from firing our missiles with reckless abandon?

Luckily, Rails provides a couple tools to manage the issue: and .

: The BlackList

Using , you can specify which fields may never be mass-ly assignable:

class User < ActiveRecord::Base attr_protected :can_fire_missiles end

Now, any attempt to mass-assign the attribute will fail.

: The WhiteList

The problem with is that it's too easy to forget to add a newly implemented field to the list.

This is where comes in. As you might have guessed, it's the opposite of : only list the attributes that you want to be mass-assignable.

As such, we can switch our class to this approach:

class User < ActiveRecord::Base attr_accessible :first, :last, :email end

Here, we're explicitly listing out what can be mass-assigned. Everything else will be disallowed. The advantage here is that if we, say, add an flag to the model, it will automatically be safe from mass-assignment.

As a general rule, you should prefer to , as it helps you err on the side of caution.

Mass Assignment Roles

Rails introduced the concept of mass-assignment "roles". The idea is that you can specify different and lists for different situations.

class User < ActiveRecord::Base attr_accessible :first, :last, :email # :default role attr_accessible :can_fire_missiles, :as => :admin # :admin role end user = woaknb.wz.sk({:can_fire_missiles => true}) # uses the :default role woaknb.wz.sk_fire_missiles #=> false user2 = woaknb.wz.sk({:can_fire_missiles => true}, :as => :admin) woaknb.wz.sk_fire_missiles #=> true

Application-wide Configuration

You can control mass assignment behavior in your application by editing the setting within the file.

If set to , mass assignment protection will only be activated for the models where you specify an or list.

If set to , mass assignment will be impossible for all models unless they specify an or list. Please note that this option is enabled by default from Rails forward.

Strictness

Beginning with Rails , there is additionally a configuration option to control the strictness of mass assignment protection: .

If set to , it will raise an any time that your application attempts to mass-assign something it shouldn't. You'll need to handle these errors explicitly. As of v, this option is set for you in the development and test environments (but not production), presumably to help you track down where mass-assignment issues might be.

If not set, it will handle mass-assignment protection silently - meaning that it will only set the attributes it's supposed to, but won't raise an error.


Rails 4 Strong Parameters: A Different Approach

Mass assignment security is really about handling untrusted input.

The Homakov Incident initiated a conversation around mass assignment protection in the Rails community (and onward to other languages, as well); an interesting question was raised: does mass assignment security belong in the model layer?

Some applications have complex authorization requirements. Trying to handle all special cases in the model layer can begin to feel clunky and over-complicated, especially if you find yourself plastering all over the place.

A key insight here is that mass assignment security is really about handling untrusted input. As a Rails application receives user input in the controller layer, developers began wondering whether it might be better to deal with the issue there instead of ActiveRecord models.

The result of this discussion is the Strong Parameters gem, available for use with Rails 3, and a default in the upcoming Rails 4 release.

Assuming that our missile application is bult on Rails 3, here's how we might update it for use with the stong parameters gem:

Add the gem

Add the following line to the Gemfile:

gem strong_parameters

Turn off model-based mass assignment protection

Within :

woaknb.wz.skist_attributes = false

Tell the models about it

class User < ActiveRecord::Base include ActiveModel::ForbiddenAttributesProtection end

Update the controllers

class UsersController < ApplicationController def update user = woaknb.wz.sk(params[:id]) if woaknb.wz.sk_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params woaknb.wz.ske(:user).permit(:first, :last, :email) end end

Now, if you attempt something like , you'll get an error in your application. You must first call on the hash with the keys that are allowed for a specific action.

The advantage to this approach is that you must be explicit about which input you accept at the point that you're dealing with the input.

Note: If this was a Rails 4 app, the controller code is all we'd need; the strong parameters functionality will be baked in by default. As a result, you won't need the include in the model or the separate gem in the Gemfile.


Wrapping Up

Mass assignment can be an incredibly useful feature when writing Rails code. In fact, it's nearly impossible to write reasonable Rails code without it. Unfortunately, mindless mass assignment is also fraught with peril.

Hopefully, you're now equipped with the necessary tools to navigate safely in the mass assignment waters. Here's to fewer missiles!

Leave a Comment

(0 Comments)

Your email address will not be published. Required fields are marked *