Thanks again Ryan for this screencast. That one was very interesting and the solution if so easy to setup... Nice !
Amazing! I imagine this may and actually WILL be overlooked on many rails sites.
very nice!every day I open this site.
非常漂亮!每天我都来这个站点.
Very helpful. I had no idea ActiveRecord relationships could be hiijacked like that. I guess it's better to use a white-list than a black-list since you never really know what attributes are available in your models.
Great - nice plain English description of what not to do, and more importantly, WHY!
Thank you!
I've downloaded all the video, and find them delicious!
BTW,
Will you please share some testing experiences with us?
So if it's not through mass assignment, and you need to declare someone an admin, or set someone as an owner of the comment - then would you place it manually in the controller?
@comment.user_id = current_user
What about the admin? How would that be set?
Great tips! Thanks!
Thanks for visualizing http://www.rorsecurity.info/2007/03/20/do-not-create-records-directly-from-form-parameters/
This security series is excellent and this episode is the best of the bunch so far. I really like it that you showed how a hacker might abuse the site. I think it really helps developers see how easy it is to formulate attacks. That should me addressing security more of a concern. Well, I hope...
@Daniel,
Yes, you can set someone as the owner of the comment that way. Or better you could build the comment through the user:
@comment = current_user.comments.build(params[:comment])
If you want to set the user admin attribute you could do it like this:
@user.admin = true
Ryan,
I think this is perhaps the best screencast yet. I especially appreciated the curl examples showing what a malicious user might do. Now I'm off to add attr_accessible to my models.
Great screencast , I love this site!!!
Question:
Why not just verify that the metod is post like this?
verify :method => :post, :only =>[ :destroy, :create, :update ], :redirect_to => { :action => :list }
This seems more simple and this way you can not create new users using curl (or the browser address field)
Or I'm I just not getting it?? :)
Cheers
Martin,
Curl can send information via a POST request, so that wouldn't stop a malicious user. Ryan's suggestion is definitely worth looking into...
Ryan,
A bit off topic, but can you post the .css code for this episode? I'm sure it's pretty simple, but I haven't done much .csss so the more examples I can see the better.
thx
Mike
@Mike, the CSS code is very similar to the one I use in the episode about the generator script. You can find the episode and the code here:
http://railscasts.com/episodes/58
The file is in "app_layout_generator.zip".
Ryan,
Thanks for the link.
Another q, might be a curl related issue.
When I submit a new user through the web browser (I'm just using a barebones scaffolded interface), I see in the dev log 3 entries, one each for the Controller#new, #create, then #list actions.
But when I try using curl as per your example, I'm only getting an #index log entry when I pass ../users as the last URL element.
If I pass .../new as the last element, I get a #new log entry, but not a #create.
If I pass ../create I do get a #create log /controller entry, and the log shows the parameters ARE passed, but the SQL statements are inserting NULL values !
This is all before I add any of the modifications you suggest for protection.
Any ideas ?
Mike
@Mike, unlike a web browser, curl doesn't automatically follow redirects. That is why you're seeing both "create" and "index" in the log when using a web browser, but only "create" when using curl.
If you're getting null values when using curl, I'm guessing it's because the attribute names aren't correct. Make sure you're naming the attributes in curl exactly how I'm doing in this episode (model name followed by the attribute name in square brackets).
Normally you don't need to bother with curl, I just used it for demonstration purposes to show how a hacker might break into a site.
Ryan,
I got it working ;)
After getting something happening with the '/create' stuck on the end of the curl command, the problem was that I was passing 'users[name ...' instead of 'user[name...'.
That pluralization stuff again !
I figured it out by looking at the Html source for the form code and also with the help of a brief curl tutorial that had an example http://curl.haxx.se/docs/httpscripting.html
At first I thought mabye it was because the web-browser created data was passing "commit"=>"Create","action"=>"create", "controller"=>"testusers"} parameters, and the curl commands wasn't, but that wasn't the problem.
I'm still wondering how your example worked ... I see that you have a "redrecting message" popup, so I assume you put a redirect inside your users.rhtml file to create .. just for the purpose of this demo ?
Mike
P.s. I also wasted a lot of time trying to get 'rake db:migrate' to work, might I suggest you do a railscast on how it works, specifically the versioning system. I found out on the railsforum that the magic commands were "rake db:migrate VERSION=0 ... folowed by rake db:migrate".
@Mike, glad you got it working. I the redirect is done in the controller after the model saves. It isn't done in an rhtml file.
On top of restricting mass assignment with attr_accessible and attr_protected, why not do something like below, restricting access to fields on a method by method basis?
params[:user].delete_if { |key, value|
!([:company_name,:profile].include?(key))
}
user.update_attributes(params[:user])
This could be done as an application helper method that takes an array and a hash.
After typing the above though, I kind of realize filtering out attributes by method might be pointless, when really the goal is to filter by user or role. A user with access to a method might have no business running it on a particular row,. The need to check permissions for the requested row will often be there, but if your doing that, why not just create a function that checks for permissions by row and field, and filters the params hash to suit?
One could pass this params filter method an ActiveRecord object, login credentials, and the params hash - to get back a sanitized params hash without the attributes the user has no access to.
Another method might identify if a particular user has access to an array attributes for a given ActiveRecord object/row, returning true or false. AJAX apps could use this to dynamically add edit links for row/attributes where the user has permission.
Does something like this exist already? Perhaps it is just overkill, usually there are just admins and simple users, where simple users are restricted the same fields but different subsets of rows. But sometimes you do end up with with a third "power user" tier that requires less than complete access to all the fields and rows.
Getting back to the original subject of mass assignment, preceding mass assignment with this params filter would make it safe to use wherever, keeping things a litte simpler on the code end by pushing permissions related stuff into the database.
After seeing this screencast last week I created a simple plugin to search your Rails projects for this exploit and I thought some of you may find it useful. I called it audit_mass_assignment:
http://code.google.com/p/audit-mass-assignment/
rake audit:mass_assignment
The audit goes through your models and checks for attr_accessible. If the model doesn’t use it, the model fails the audit. The plugin is obviously only for people that use the whitelist approach.
I could see a whole family of “audit” plugins that could check certain aspects of Rails projects for bad/insecure patterns. You could include (or omit) any kinds of audits that you want and run them before you deploy your app with “rake audit”. Obviously not every exploit or bad pattern can be audited this way but many can be.
@Ryan
@user.admin = true
Okay, I understand that. But that still strikes my curiosity. Basically any time an attribute needs to be updated by the user, it can be hacked by a user if they know the column field, or whatever sets it.
So, how would you protect the ability for an admin to make another user an admin or something similar?
If it's in the form that means any user can use the same form and gain those privileges no?
Another example:
Let's say a user has a post, they want to make it either public or private. Since all users have access to that form, what stops them from submitting it to another user_id and another post and changing the privacy level of it? That attribute has to be accessible for it to be set a certain level of privacy.
@Daniel,
To make an attribute only settable by admins you can do this:
@user.admin = params[:user][:admin] if current_user.admin?
This way the admin parameter for the user will only be set if the current user is an admin. Does that answer your question?
this makes me want to try to hack rails sites... :-P just kidding. though it would be really cool if i could hack someone's site who i know, i'd get lots of cred for that! but honestly this was really cool because i think there's a lack of stuff addressing security concerns in rails, i've kind of wondered about things like this before but never really seen it explained... thanks
Check out my blog post on mass assignment..
http://www.snailrails.com/2008/1/mass-assignment
As I have elsewhere shown, natural causes, so far as I know, constitute a body of demonstrated doctrine, and some of this body must be known a priori, by means of analytic unity.
I have moved my audit_mass_assignment plugin to GitHub:
http://github.com/ryanlowe/audit_mass_assignment
The plugin scans the models in your project and lets you know which ones do not use the attr_accessible whitelist approach to protect attributes.
It's very handy for projects that have a lot of models, get updated sporadically or are updated by many people. An audit like this could be run as part of a continuous integration test suite.
Hi, first, thanks for all those great videos!
I'm still curious though, that means that any user would be able to create a user, only providing a name field?
Thanks ! really usefull its one kind of security....



