This is a brief review of mass assignment in Rails. See the follow-up post on Finding and fixing mass assignment problems in Rails applications for some more tips on how to find and fix mass assignment problems.
We’ll begin with a simple example. Suppose an application has a User model that looks like this:
# == Schema Information # Table name: users # # id :integer(11) not null, primary key # email :string(255) # name :string(255) # password :string(255) # admin :boolean(1) not null class User < ActiveRecord::Base validates_presence_of :email, :password validates_uniqueness_of :email . . . end
Note the presence of an admin boolean to identify administrative users. With this model, the Users controller might have this standard update code:
def update
@user = User.find(params[:id])
respond_to do |format|
if @user.update_attributes(params[:user])
flash[:notice] = 'User was successfully updated.'
format.html { redirect_to(@user) }
else
format.html { render :action => "edit" }
end
end
end
This works fine, but note that the line
if @user.update_attributes(params[:user])
performs an update to the @user object through the params hash, assigning all the @user attributes at once—that is, as a mass assignment.
The problem with mass assignment is that some malicious [cr|h]acker might write a script to PUT something like name=New+Name&admin=1, thereby adding himself as an administrative user! This would be a Bad Thing™. The standard solution to this problem is to use attr_accessible in the model to declare explicitly the attributes that can be modified by mass assignment. To protect our User model, for example, we would write
class User < ActiveRecord::Base attr_accessible :email, :name, :password validates_presence_of :email, :password validates_uniqueness_of :email . . . end
Since :admin isn’t included in the attr_accessible argument list, the User model’s admin attribute is safe from unwanted modification.
This seems simple enough, but the rub is that remembering to protect against mass assignment is difficult. Using mass assignment doesn’t affect the normal operations of the site, so it’s hard to notice the problem. Moreover, although you could shut off mass assignment globally, often there are many models that are used internally and never get modified directly by a web interface. Not being able to use mass assignment for these models is inconvenient, and manually making all attributes attr_accessible is cumbersome and error-prone. So, what’s a Rails developer to do?
Spurred by an email from Eric Chapweske of Slantwise Design, I recently audited the Insoshi social network for mass assignment vulnerabilities. Doing this manually was annoying, so in the process I developed a simple plugin to find likely vulnerabilities automatically, by searching through the controllers for likely mass assignment and then looking in the models to see if they didn’t define attr_accessible. The result is a list of potential trouble spots.
To use the find_mass_assignment plugin, simply install it from GitHub as follows:
$ script/plugin install git://github.com/mhartl/find_mass_assignment.git
(You’ll need Git and Rails 2.1 or later for this to work.) The plugin defines a Rake task to find mass assignment vulnerabilities; running it on the example Users controller from above would yield the following:
$ rake find_mass_assignment /path/to/app/controllers/users_controller.rb 5 if @user.update_attributes(params[:user])
This tells us that line 5 in the Users controller has a likely mass assignment vulnerability.
The find_mass_assignment plugin doesn’t fix mass assignment problems automatically, but by making it more convenient to find them I hope it can significantly improve the odds that they will be caught (and fixed!) quickly.
[...] alerting me to mass assignment vulnerabilities in the Insoshi social network sourcecode. (See my post on mass assignment for a quick review of the concept.) I quickly set to work fixing the problems, and within a few [...]
Pingback by Finding and fixing mass assignment problems in Rails applications « Insoshi Ruby on Rails blog — 2008-09-21 @ 18:22