Do not create records directly from form parameters
Tuesday, March 20, 2007 at 6:53PM The scaffold generator creates code like the following, which is allowedly easier to handle, but vulnerable:
@user = User.new(params[:user])
With this code, Rails will create a new user based on the values that the user entered. Any corresponding attributes in the parameter hash params will be set in the user model. Arbitrary properties of the new user can be set by an attacker, the user's privileges, for example. Given you have a user registration form like this:
<form method="post" action="http://www.website.domain/user/register">
<input type="text" name="user[name]" />
...
</form>
An attacker could change the form (by saving it to disk, for example) to the following:
<form method="post" action="http://www.website.domain/user/register">
<input type="text" name="user[name]" />
<input type="text" name="user[admin]" value="1" />
...
</form>
If the attacker knows that the User model has an “admin” column, the newly created user will have administrator rights. One solution to this problem is, not to use mass-assignment and assign each value individually.
User.new(:first_name => params[:user][:first_name])
Another solution is, to protect several properties so they can't be assigned using mass-assignment, but have to be set individually. The following line in your model will protect the “admin” attribute, i.e. it will be ignored during mass-assignment.
attr_protected :admin
If you want to set a protected attribute, you will to have to assign it individually:
@user = User.new(params[:user])
@user.admin = false
You can also use the whitelist approach (highly recommended), which allows attributes to be mass-assigned, instead of forbidding access to them. Use attr_accessible with the attributes you want to allow access to, instead of attr_protected to do this.


Reader Comments (148)
people should be testing their controllers for this stuff, but likely aren't.
If you don't want to use the broad kudgel that is attr_protected, you can also use hash filters like I devised here: http://blog.caboo.se/articles/2006/6/11/stupid-hash-tricks
My vote is for the whitelisting approach of . The safer approach for form validation (and other tasks like configuring a firewall) is to deny everything first, with specific exceptions for input you expect.
Assigning things individually is only marginally more secure, given the fact that your example contains no server side validation of data or authorization. Almost invariably, your application would have some sort of administrative interface to choose set admins, and that action could be divined in the same way that the admin attribute could be. Granted, guessing that you post to /users/make_admin/1 to make an administrator is more obscure than guessing the user[admin] field, but we know better than security through obscurity.
The bottom line is that you should check your incoming data. even something as naive as the following is much more secure:
My first paragraph seems a bit combative; I don't mean it to be, but can't find a sufficient alternative to the warning. I'm firmly in the camp that the less code I have to write the better, and if I can funnel all of my changes to a given model through one vanilla action, so much the better. That means there is only one place to secure properly, one place to update as things change, and one place to test. If you package your authorization code into some declarative-style extensions to your controllers, the code can be quite simple, flexible, and composable.
My ideal psuedo-code would be something like:
-Scott Fleckenstein
Well that didn't work out so well, lets try it with pres:
First broken code block
before_filter :only => [:update, :create] do |c|
raise "access_denied" if c.params[:user].has_key?("admin") && !logged_in_user.admin?
end
second broken code block:
class UserController :unauthorized_access # see the rails plugin exceptional (http://nullstyle.com/home/exceptional)
def update
User.update(params[:id], params[:user])
end
end
Thank you for this entry. May I suggest the use of @params is deprecated since a while now. ( use params instead).
It's amazing how many potential security holes exist in every web application. Of course I've fixed this one in my application now - thanks to you!
Keep up the good work - can't wait for your next posting ;-)
[...] for your comments. Scott writes in a comment: Assigning things individually is only marginally more secure, given the fact that your example [...]
I stumbled upon the mass assignment issue too while working on a community site in Rails for a project at my university. I used the SaltedHashLoginGenerator and found it lacking a lot of the issues discussed here. BTW, it's a great site, I wish I had found it earlier !
My simple solution was to make a whitelist as an Array and just delete everything not in there from the params hash...
I found this Firefox Add-on very useful for testing attacks:
https://addons.mozilla.org/en-US/firefox/addon/966
Ryan at Railscasts.com has a video dealing with mass assignment.
Check it out:
Adderall....
Purchashing xanax with mastercard. Cheap generic xanax 2mg bars. Xanax....
Herbal phentermine natural weight .... Herbal phentermine. Does herbal phentermine work frontier pharmacies....
Cheap phentermine online....
Buy valium online without prescription. Buy valium with mastercard. Buy valium. Buy valium online save wholesale price yep. Buy valium online....
Lowes t online phentermine price. Online phentermine. Wrapper phentermine online. Order phentermine phentermine online....
Phentermine 37 5mg. Buy phentermine online 37.5mg no prescription....
Phentermine diet pills....
Buy phentermine online with paypal. Online phentermine and no prescription. Online phentermine without contacting doctor. Online phentermine....
Tramadol hcl. The lowest tramadol hcl price guaranteed fast....
Online no prescription valium. Valium without prescription. No prescription needed for valium. No prescription generic valium. Valium no prescription master. No prescription valium. Valium prescription online....