Alternative to mass assignment for HABTM - ruby-on-rails

I have a similar situation to this question, which was already posted. Koraktor has asked
I'm using a simple model for user authorisation with two ActiveRecords User and Role User and Role have a HABTM relation to each other.
.
.
Manually assigning roles with #user.roles or #user.role_ids works, but not the "magic" inside User#new or User#update_attributes.
Oleg suggested that
attr_accessible :role_ids
be added to the user model. This would allow mass assignment operators to update roles. However, he cautioned against using this approach because of security concerns.
I have a follow up question to Oleg's response -
In this situation, is there a recommended method to update roles without using mass-assignment?
Also, assuming
you authenticate users,
only allow administrators to CRUD users by putting a before_filter in the users_controller,
is mass-assignment of role_ids still a valid concern?

Mass assignment is a feature of Rails provided for using less code for updating a model like this
Model.create(params[:model])
#model.update_parameters(params[:model])
instead of
#model.field1 = params[:model][:field1]
#model.field2 = params[:model][:field2]
...
#model.save
But with this feature, comes the risk of updating values which we dont intend. For example, if we want just field1, field2 and field3 to be updated by the user and you use update_parameters for mass assignment, there is a risk of updating field4, if one is passing model[user][field4]=some_value either from url or by any other ways. If we are explicitly assigning the fields in the code, we do not have this risk. But then we will have to set the values for each field(wherever we are updating or creating) which is not very productive.
So, for using the mass assignment feature, we have 2 options. First is attr_protected
attr_protected :field4
This will protect field4 from being saved from mass assignment from params[:model] even if it contains field4. For saving field4, we have to call the setter for field4 explicitly in
the code(#model.field4 =). But, the problem with attr_protected is that Rails may provide some other attributes that we may not know for mass assignment. For example, if we define
has_many :model2s
in Model.rb, Rails will provide a method model2_ids= automatically and this can be accessed by mass assignment.(If we give model[model2_ids]= in the url, it will create associations, not intended at all). So, there is a chance of missing attributes like this while using attr_protected.
So, the recommended method is to use attr_accessible
attr_accessible :field1, :field2, :field3
This will make these fields open for mass assignment and all other attributes in the model not available for mass assignment. So, the recommended way is to make those attributes which we are providing in a form for users to edit as attr_accessible and all other parameters will be protected. But, while using this you have to make sure you have included all the attributes you need for edit as attr_accessible.
In your case, since you want the user to edit the role_ids and you are providing access to CRUD for admin users only, I think you can use attr_accessible :role_ids. The alternative would be to assign role_ids explictely like .role_ids = params[:user][:role_ids]. You should use this, if you have another form where you dont want the user to edit the role_ids.

Related

Custom field in devise without mass assignment

I am using devise for authentication but have also added a display_name field that I don't want the user to be able to edit after creating registration/sign up. The problem is unless I add this column/field with attr_accessible in the model, I get the following error.
Can't mass-assign protected attributes: display_name
But I can't enable attr_accessible because that will open the app up to mass assignment hack in through the edit method.
I thought about overriding Devise::RegistrationsController to supplement devise's create method, as shown in this gist, but filtering the params before calling super will obviously result in the display_name not being saved at all, and the field would show up empty in a form after validation fails for any reason.
Is there method I can override or a hook I can add into the model to prevent mass assigment of a single variable in certain conditions? Perhaps I can remove the display_name from the parameter list before_validation and put it back after_validation, while also validating it with a technique like this, and then doing a normal validation-less single assignment? Of course this still wouldn't prevent a mass assignment hack through the edit method, so I'd have to add a signature to params[:user] hash in the create method, which would be checked before removing/adding display_name from params when validating/updating the display_name attribute by itself. This seems like a mess to me, is there a better way to control mass assignment in the controller while using devise?
One approach that would be quick and dirty would be to override Devise::RegistrationsController like I mentioned first and simply cut and paste the original create method from the original devise registrations_controller.rb source, and manually add/remove display_name from params[:user] before and after the build_resource(sign_up_params) call. Just before the save call, I'd also have to add display_name individually to the model object and use single attribute validation hackery mentioned above if I want validation on display_name to work. If I update devise in the future this approach has a high probability of breaking the create method, so I've ruled it out.
After typing out these various approaches I've realized I will probably override the edit method in Devise::RegistrationsController and raise an error if params[:user][:display_name] or any other attribute not in attr_accessible is set. This sort of blacklist in multiple places approach also rubs me the wrong way, it will be too easy to forget adding this to new methods during future expansion, even if the blacklist is kept DRY in a separate private method. Still, this appears to be the least convoluted way.
Is there another approach worth trying?
Mass assignment protection only protects from assigning parameter via methods like create, save or assign_attributes, which all take a hash of arguments as an argument. You should still be able to use
user.display_name = value
user.save
in your create action.
If I don't misunderstand, your goal is just that forbids to modify display_name when updates the user.
You can put display_name in attr_accessible and have a hack in before_update:
class User
before_update :bu
def bu
if changed? && changed_attributes['display_name']
self.display_name = changed_attributes['display_name']
end
end
end
Prevent display_name to be modified, but there is no way to modify display_name after user is created.
You probably need to add an attribute as a switch and improve before_update to avoid this situation.

attr_accessible, attr_accessor, I would like to know what they do

I'm doing my first steps in Rails and in object-oriented programming.
There is something quite fudemental that I would like to understand: why do we need attr_accessible within the model?
I have read that hackers can use mass-assignment in order to change database entries and therefore compremise security, and that's why sensitive fields need protection (using atribute_protected in this case).
Is attr_accessible the opposite of atribute_protected? If so, why do we need to state which fields are accessible and which are not? aren't those fields accessible by defult? And what is attr_accessor used for?
I noticed that if I don't make some fields acessible, my application doesn't run. Can I use attr_acessible for sensitive fields like :password_digest and :admin?
It would be amazing if someone could explain it to me.
All the best,
TimmyOnRails
You've got a couple of concepts mixed together here, so I'll try to untangle them.
attr_accessor is for setting up a readable and writable attribute. It is the equivalent of saying attr_reader and attr_writer. Since your question isn't directly about attr_accessor, I won't address it anymore than saying check out this link on Accessors.
According to the Rails docs: attr_accessible is the opposite of the attr_protected macro
You're correct that these methods are used to prevent Mass Assignment vulnerabilities.
attr_accessible says which attributes can be set by mass assignment.
attr_protected says which attributes cannot be set by mass assignment.
So what's the use case for each? In one case you're able to set a global config option that makes it so that all attributes must be declared attr_accessible:
config.active_record.whitelist_attributes = true
In that case you'd use attr_accessible frequently.
And attr_protected? If you went the opposite way and said false on whitelisting attributes, how would you declare which attributes shouldn't be mass assignable? If you said attr_protected you're right! :D
Typically you'd want to set fields like :admin as attr_protected because you don't want an attacker coming in and escalating their privileges to an admin role.
Mass assignment is not something that's easy to get right. Big, smart development teams have gotten this wrong. So tred carefully and make sure you understand what's going on!

How does this stop mass assignment?

I wanted to start using attr_accessible with my models to stop the problem with mass assignment. I understand how it works and have researched as much as I could.
What I don't understand is the difference between using update_attributes(params[:my_form]) or create(params[:my_form]) and setting the fields one by one? Aren't both just as vulnerable?
What is the difference between NOT having attr_accessible and doing this...
#model_object = ModelObject.new
#model_object.create(params[:model_object_params])
And having attr_accessible and doing this...
#model_object = ModelObject.new
#model_object.field1 = params[:model_object_params][:field1]
#model_object.field2 = params[:model_object_params][:field2]
#model_object.field3 = params[:model_object_params][:field3]
#model_object.save!
Aren't both these methods of creating the record just as vulnerable? The hacker/cracker could send a url to both these methods and both would do just the same, right?
Or does using attr_accessible and updating the fields one-by-one do something different or somehow become safer?
There's where all these methods I'm finding of using attr_accessible don't make any sense to me. It seems to be doing the same thing two different ways. What am I missing?
Thanks.
In the way you are doing it, it does not prevent "mass assignment".
"Mass assignment" is the term used when Rails is handling the assigning of values to attributes in a model. This is typically done in a controller, using the names and values in params.
When you're doing the assigning yourself, it is also "mass assignment", in a way; but you have fine control over what to assign and what not to in this case. So, to save writing that boilerplate assignment code, Rails provides attr_accesible - same control, less code.
To see how it is used:
Presume that a ActivityLog model has an attribute called user_ip_address.
Now, user_ip_address is an attribute in the model, and could be assigned by mass-assignment or by "self-rolled-mass-assignment".
But in both cases that is wrong -- you don't want user-supplied input to set a value for that attribute.
Instead, you want to always find out the actual IP address of the user and assign that value (ignoring any
value in params). So you would exclude user_ip_address from attr_accessible and instead assign it yourself.
attr_accessible :all_attributes_except_user_ip_address
#al = ActivityLog.new(params[:model_object_params])
#al.user_ip_address = get_origin_user_ip_address
#al.save
For any information that a user should not be able to change, use attr_accessible and exclude it from the list.
The short answer is that it stops field4 from being set implicitly.
The difference is that without attr_accessible a hacker could update a field that is not in your form. With attr_accessible this impossible.
E.g. if your user model has a field is_admin, a hacker could try to create a new admin by posting:
params[:user][:is_admin] = true
If attr_accessible is set (and obviously it shouldn't contain is_admin) this is impossible.
About your example: if your model only has field1, field2 and field3 and there are no other database columns you want to protect, there is no need to use attr_accessible. Hope this makes it clear.
Just remember:
Without any precautions
Model.new(params[:model]) allows
attackers to set any database column’s
value.
Source: http://guides.rubyonrails.org/security.html#mass-assignment
The idea here is to limit the parameters you will accept for a given model. Then you can test each of them either with a validation or some other code to be sure they fit expected values.
Attr_accessible is intended to limit the "surface" of your model to what you intend to accept and check carefully. In this way you can automatically ignore an injected parameter like :roles => "admin" in case you add that feature to your model
user.update_attributes(params[:user])
Since the roles attribute is not listed in attr_accessible, the user's attempt to become an administrator is fruitless.
You want to handle the validation logic in one place (your model), instead of checking each parameter value in your controller.
Mass assignment isn't something you prevent, it's something you control. It's a good feature, one that makes things easier and cleaner, but without some kind of ability to control what gets set via mass assignment it's a potential security hole. attr_accessible, as others have mentioned, provides that control.

Which fields should be protected from mass assignment?

I'm doing a security audit on a Rails 2.3.8 application and one of the things that has come up is our lack of attr_protected or attr_accessible in my model definitions. I understand the reasoning behind them, and have even written a gem to assist with mass assignment, but I'm looking for attributes that I might potentially be missing.
The problem I have is determining which fields should be protected. Are there any rules that people generally follow for this? I'm thinking attributes like foreign keys and booleans like admin? would make sense to protect. I'm also wondering about fields like STI type, and polymorphic *_type/*_id fields. I see that Rails 3 introduced attributes protected by default, but it doesn't appear that Rails 2.3.8 has this.
Any guidance on this would be greatly appreciated.
My general rule of thumb is that any attribute you don't want users to modify should be protected.
So in my models, I use attr_accessible for all attributes that are present as fields in forms. All others are protected. (I'd rather have everything protected by default.)
In other words: assume all data sent by clients will be maliciously tampered.
Edit: relevant blog post http://www.kalzumeus.com/2010/09/22/security-lessons-learned-from-the-diaspora-launch/
I guess every "potentially dangerous" attribute should be protected, like an admin flag for a user.
In my opinion, ideally, every model should have attr_accessible for all attributes that can be updated. It's the safer solution.

How do you use attr_accessible?

I can't find any tutorials for newbies. Aren't there any simple rules for using attr_accessible? Should I use it for attributes that users can set from forms?
What if all attributes can be set from forms?
I would appreciate a link or short advice. I already found http://asciicasts.com/episodes/26-hackers-love-mass-assignment
attr_accessible is a white list of attributes that can be mass assigned to the model. It is a strategy which says you need to explicitly list all the attributes. This way the "open ports" are well known and listed in the model clearly. This is opposite of attr_protected which is a black list of fields to be protected from mass assignment.
Often in even moderate to simple application there are foreign key type fields such as user_id or company_id which may not be determined by user input. Those fields must be protected from user input. Primary key field 'id' is normally protected by Rails anyway.
If your model has all columns that can be updated by Form input, then sure go ahead a list them with attr_accessible (or you may want to skip attr_accessible for this particular model).
Don't throw the baby out with the bathwater however, attr_accessible is a good thing and ensure that you use it in other models which may not be as open as the one you are talking about in the your question.
I generally use attr_protected on models with a large number of columns and attr_accessible on most others.
Check this out: Use attr_protected or we will hack you

Resources