Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mass assignment vulnerability - how to force dev. define attr_accesible? #5228

Closed
homakov opened this issue Mar 1, 2012 · 109 comments
Closed

Comments

@homakov
Copy link
Contributor

homakov commented Mar 1, 2012

Those who don't know methods attr_accesible / protected - check that article out http://enlightsolutions.com/articles/whats-new-in-edge-scoped-mass-assignment-in-rails-3-1

Let's view at typical situation - middle level rails developer builds website for customer, w/o any special protections in model(Yeah! they don't write it! I have asked few my friends - they dont!)
Next, people use this website but if any of them has an idea that developer didnt specify "attr_accesible" - hacker can just add an http field in params, e.g. we have pursue's name edition. POST request at pursues#update

id = 333 (target's pursues id)
pursue['name'] = 'my purses name'
pursue['user_id'] = 412(hacker id)

if code is scaffolded than likely we got Pursue.find(params[:id]).update_attributes(params[:pursue]) in the controller. And that is what I worry about.

After execution that POST we got hacker owning target's pursue!

I don't mean that it is Rails problem, of course not. But let's get it real(Getting Real ok) - most of developers are middle/junior level and most of them don't write important but not very neccessary things: tests, role checks etc including topic - attr_accesible

how to avoid injections ? What should Rails framework do to force people to keep their rails websites safe? Making attr_accesible necessary field in model? What do you think guys.

@drogus
Copy link
Member

drogus commented Mar 1, 2012

I was thinking about generating something like this, unless you pass some kind of option to generator (sometimes you just want to skip attr_accessible if you know what you're doing):

# You should always whitelist your accessible attributes for security
attr_accessible nil

@drogus
Copy link
Member

drogus commented Mar 1, 2012

@lest pointed me to #4062, there is a config setting that can do something like that config.active_record.whitelist_attributes. Although, based on that discussion I think that the consensus is that it should not be the default.

@drogus drogus closed this as completed Mar 1, 2012
@drogus drogus reopened this Mar 1, 2012
@homakov
Copy link
Contributor Author

homakov commented Mar 1, 2012

so, here is what i came up to.
My main concern - is relations.
I hate the idea that some people could rewrite object references by themselves. Nobody should be able to set task[user_id]=hacker_id or sort of
Vulnerable references are the main evil because

  1. it totally ruins database and leaves no way to fix it back but backups
  2. it works clearly. Hacked user won't be able to figure out "Where is my blog post" or "How come this spam post is in my timeline written by me" (if newpost[user_id]=target_user_to_hack)

I suggest to have foreign keys and primary keys protected by default(what are keys can be recognized by ActiveRecord easily).

IMO Rails should teach developers to set up so important and vulnerable keys by hands. It is not a handicap for prototyping, just +a few lines of code. But it is all about sane and safe rails apps.

@task = Task.new
@task.user = current_user
@task.update_attributes(params[:task]) #having params[:task][:user_id] should raise error

#or projects-tasks relations
@project = Project.find(params.delete(:project_id)) #we have project_id but it is protected. lets delete to avoid exception
if @project.user == current_user
  @task.project = @project
else
  #alarm!
end
@task.attributes = params[:task]

I've got a big picture in my head but the backbone is described above. I strongly believe that having keys(references, relations) will make ruby on rails a 20% more awesome. I mean robust

@homakov
Copy link
Contributor Author

homakov commented Mar 1, 2012

Oh, didn't mention. On my part - I wouldn't support setting all attrs protected. It will cause such big mess! You cannot force every body to read release notes. Stackoverflow will be down. :] + it is dirty solution.
What also we have in model but keys? E.g.
title: string - not vulnerable at all, user is able to edit his comment
rating: integer - pretty vulnerable but not dangerous
account: integer - developer who left user[account] open for mass assignment should suffer. Nevermind about him.
what else? Do we really need protected fields by default? No.

@homakov
Copy link
Contributor Author

homakov commented Mar 1, 2012

@dhh you dont like 'whitelist' idea - i dont too. But what is your opinion regards relation keys? They are obviously should be defined manually - in most cases. That is why having them protected by default is not a big deal.
And, surely, it should be opt-out-able, e.g.
config.active_record.protect_keys = true

@homakov homakov mentioned this issue Mar 2, 2012
@homakov
Copy link
Contributor Author

homakov commented Mar 2, 2012

Just imagine which power this vulnerability gives in my.. and now in your hands if you get what has happened.
@dhh @josevalim Y you no care :trollface:

@sikachu
Copy link
Member

sikachu commented Mar 2, 2012

There're too much trolling going on in this ticket and I'm not sure anymore if you mean anything serious. If you're going to do a bug report or feature request, just stop trolling and try not to be annoying, then the core team will come and check out by themselves.

Name calling while trolling doesn't help, trust me.

@homakov
Copy link
Contributor Author

homakov commented Mar 2, 2012

@sikachu bugs are serious.

This is not only bug report, because this problem is so wide spreaded. postereous, speakerdeck, scribd, github - and I only have started testing.

We need to introduce blacklist attributes. MOst of rails apps(from small to github) likely got mass-assignment bugs if they don't user attr_accessible.

I just want to attract more attention to reviewing this problem from scratch and calmly decide - what should we do with M As-ment problem.

And, if some of you support my idea I could provide fixing pull request. But you ignore this bug, like nothing has happened for a long time. SOrry for not called for trolling, I overplayed :) Everyone, again, express your point please, you are welcome.

@fxn
Copy link
Member

fxn commented Mar 2, 2012

There was a proposal about changing that flag in #4062 and the consensus is the pros of the default configuration outweigh the pros of the alternative.

Thanks!

@fxn fxn closed this as completed Mar 2, 2012
@homakov
Copy link
Contributor Author

homakov commented Mar 2, 2012

@fxn have you read my post? Did you see vulnerabilities i pointed out in e.g. github?
It is not about the flag in configuration, I'm pretty certain. We need to have :created_at, :updated_at, and references to be protected by default. Whitelist issue has nothing to do with my point
Please read issues carefully, because I clearly emphysized that flag is not a panacea

@fxn
Copy link
Member

fxn commented Mar 2, 2012

What I want you to see in that thread I mentioned is the way the core team perceives this. You are not discovering anything unknown, we already know this stuff and we like attr protection to work the way it is.

@homakov
Copy link
Contributor Author

homakov commented Mar 2, 2012

look. this is github, written on rails. https://github.com/homakov/ClientSit/issues/4
as you see github is vulnerable. Because gh devs are bad? Or who's in charge?
Rails are in charge, and blacklist of custom [:created_ate, :updated_at] is the least we MUST do

Really, is there any case when user should be able set created_at by himself? It is timestamp! Why I can set it using just <input name=model[created_at] value='1987...' ?
Again, let's talk about certain vulnerability-case. :created_at MUST be protected by default. Am i right?

@fxn
Copy link
Member

fxn commented Mar 3, 2012

Rails is not in charge, it is your responsibility to secure your application. It is your responsibility to avoid XSS, to ensure that the user is editing a resource that belongs to him, etc.

Rails, however, does a lot of effort to assist you in securing your application as much as it can. That's why you have some protection measures built-in.

I don't think we need to special-case anything. The user has a flag to secure by default, I personally think that is enough. I see little benefit in special-casing the timestamps if you have the flag set to false. And I am totally -1 in doing anything fancy with foreign keys because they have of course valid use cases where users can set them.

@homakov
Copy link
Contributor Author

homakov commented Mar 3, 2012

Perfect response. Rails are not in charge - my question was like, 'why is that'. Surely nobody is fully in charge.

But only rails apps got this kind of bug. Yeah, phpists never got so fast prototyping tools so most of them cannot even dream about update_attributes {} and form generation too.

Rails has very convinient tools, I agree! attr_accessible is good and must thing for every developer. But for now we got it too unknown. Most of middle level developers forget about the meaning of this tool. Mixes with attr_accessors in ruby itself - so their model gets very unsecure.

So you only a little bit support special casing of timestamp.
For first view at the problem of timestamp-overwriting it doesn't seem fatal. But when I have issue/comment written in 3012 year I at least have it number 1 in any timeline orders. That seems ugly to me so I am keen to special case timestamps.

Regards foreign keys - I didn't get why you are "totally" -1, and if you got spare time please give me valid use cases if you know some. That will help me in understanding your point fully

another way to decide problem - populate using of accessible. Include it in scaffold generated output for model files. Im pretty sure rubyists will care much more having these lines and one day 4/5 will know+use this declaration. (now 4/5 don't either know or use, that's sad)

@drogus
Copy link
Member

drogus commented Mar 3, 2012

@homakov I also don't like the idea of doing anything automated with foreign keys or timestamps. That way, people that does not know that they need to use some kind of params protection will be secure in those 2 cases, but it will fail with any other critical field, let's say admin.

@fxn what do you think about generating a model class with a comment describing attr_accessible and giving example, something like:

# If this model can be modified by publicly available resource using update_attributes
# method, you should protect its attributes with attr_accessible, for example:
# attr_accessible :title, :body

@homakov
Copy link
Contributor Author

homakov commented Mar 3, 2012

@drogus it is your viewpoint. Ok, let's not bother junior devs who will need to think about protection.
But I like your proposal regards model generator. I suggested it as an alternative, this fix will satisfy me. :)

@rmoriz
Copy link

rmoriz commented Mar 4, 2012

It's 2012. When will people stop believing in "blacklists"?

The default has to be restrictive and the developer has to make sure she/he does acceptable exceptions. It's like with the XSS/sanitize stuff. It will never work the "blacklist"/"developer should care" way, imho.

At least throw a warning into the debug-log when a model has no attr_accessible defined at all...

@docwhat
Copy link
Contributor

docwhat commented Mar 4, 2012

@homakov Is this how you added b839657

Just curious.

@busyloop
Copy link

busyloop commented Mar 4, 2012

@rmoriz +1

Rails is all about conventions. Broken by default is not a good convention.

@bai
Copy link

bai commented Mar 4, 2012

@homakov Man, you look like a kid that wants some attention. Stop trolling and make GitHub a favor — file a private bug request via contact form — that's how non-douchebag people do.

I totally agree with @fxn — missing attr_accessible is not Rails' fault but developer's mistake. Generated models could have a friendly comment like @drogus said, but in practice I think we don't use generators that much, I thought that's easier just to type class Blah < ActiveRecord::Base.

@docwhat
Copy link
Contributor

docwhat commented Mar 4, 2012

@bai don't shoot the messenger, dude.

@rmoriz
Copy link

rmoriz commented Mar 4, 2012

@bai what you say? Every book, every tutorial, every guide tells developers to use generators. Do you create all the test + migration files by hand?!

@bai
Copy link

bai commented Mar 4, 2012

@docwhat I might have a bit overreacted — apologies for that. Yet I think giving one example of vulnerability is enough, no need to go through commits and issues over and over screwing things up.

@cmelbye
Copy link

cmelbye commented Mar 4, 2012

@bai is right, though. Zero-day attacks are a completely inappropriate way of raising awareness of a vulnerability.

@bai
Copy link

bai commented Mar 4, 2012

@rmoriz I use generators on prototyping stage extensively, but then it simply gets too custom and often beyond scaffolds. It depends on a dev though.

@hron84
Copy link

hron84 commented Mar 6, 2012

@ELeo @meskyanichi was said all argument what I would like, so I would like to reply to just this:

You can't protect the user from everything.

This is absolute true. But, as a framework developer, I can protect my users from security problems caused by framework if it is possible. Developers are not a secretaries or nurses to I shouldn't assume they can't solve their problems but if I can help them to write better and more secure application then I must do it.

@fny
Copy link
Contributor

fny commented Mar 6, 2012

Take a breath, and think naturally...

In life, people consciously and loudly choose to tote themselves in the nude. Even simple fashion is considered a statement:

attr_accesible birthday_suit # read: wildcard/regexp
attr_accesible keg, midriff, sixpack, love_handles
attr_protected sun_dont_shine, knockers, lingere, knicker_bockers
attr_protected bunny_suit # read: put the hazmat on the bunny

This is an issue of avoiding the dreaded nipslip or more poignantly protecting baby developers from rapists and pedophiles. Seriously, how much can you expect from a five-month-old?

Consider another analogy between escaped strings (which Rails quite aptly handles) and consumables. Cereal and arsenic shouldn't mix; medicines are proscribed by experts; and the LSD's for the adventurous. In the end, you choose, but you can't just allow anyone to shove anything into any orifice.

Sensible conventions reflect how we think, and thus make languages like Ruby and fameworks like Rails so wonderfully appealing and empowering. Flexibility shouldn't be compromised, of course: I'd be pissed if I had to fight to dress myself. But keep the infant in his diapers and sew the long-forgotten crotch holes in your buddy's pants (coughgithubcough.) After all, mistakes are only human. ;D

@azinazadi
Copy link

i think instead of fighting, rails people should find a nice solution for this problem. please

@hron84
Copy link

hron84 commented Mar 6, 2012

Solution is in the repo, forcing security is enabled by default.

@cmrichards
Copy link

Couldn't rails automatically use the input fields declared within the form_for block to determine what fields are allowed to be posted? This could be implemented by automatically inserting a special key at the end of the form, then the controller could only allow the declared parameters through to the actions.

@cmrichards
Copy link

Would it be possible to encode the ids of every element in the form? Using sha1 or something like that. The form could also contain something that would assist in the decoding of the ids. The helper methods (form_for , text_field) would handle the encoding and the controller would automatically decode the params, so it would be transparent to the user.

@hron84
Copy link

hron84 commented Mar 6, 2012

@evilgeenius No, it is not possible, because the name attribute what is really matters. And it is needed for compute params array. And from SHA1 you cannot decode anything, because you do not needed to name your form elements as same as attribute names - it is just a help for you. But, the form-decoding logic is cannot guess what do you want to get in your form, and if the choosen encoding is decodeable, then hacker can decode it relative easily - so it is not a protection, but security through obscurity - not needed, unusable, and does not matter.

And form_for is just generates a HTML form. It is not related to handling incoming parameters. And,you cannot restrict incoming parameters on the level of framework, because it is an application-dependent thing.

@wrzasa
Copy link

wrzasa commented Mar 6, 2012

On 06.03.2012 20:17, Gabor Garami wrote:

@evilgeenius Decoding? From SHA1? No, it is not possible. But what do you mean?

I think he meant attaching a kind of control sum to each form, to allow
controllers to automatically verify if there were any additional fields
added to the form by an attacker.

wrzasa
<

@norv
Copy link

norv commented Mar 7, 2012

Can someone please point out to me a reasonable use case, for a web application, when you don't need to make sure your POSTed data is exactly what you're expecting?

I seem to see people think there are cases where not being forced to be explicit about what you allow, would be... needed, apparently. I'd appreciate a case.

@wrzasa
Copy link

wrzasa commented Mar 7, 2012

On 07.03.2012 14:00, Norv wrote:

Can someone please point out to me a reasonable use case, for a web
application, when you don't need to make sure your POSTed data is
exactly what you're expecting?

I seem to see people think there are cases where not being forced to be
explicit about what you allow, would be... needed, apparently. I'd
appreciate a case.

There are cases when you don't care, because your model contains only the
fileds that the user is allowed to edit, so an attacker would gain nothing.
This is the only use case at least loosely connected with what you asked. I
can't see any other.

Your question is even more important in case of RoR where my use case is
never true, becasue every model contains at least updated_at and created_at
and in most cases no user should be allowed edited them.

@ghost
Copy link

ghost commented Mar 7, 2012

@norv In the real world, there aren't a lot. One exception is where the data is coming from a "trusted" user, e.g. an admin. Sure, maybe there's fields you want to secure even from an admin, or maybe not, or maybe you just figure it doesn't matter if they want to hack their own app, if they want to go to those lengths.

However, not every model deals with POSTed data, and some do but only indirectly.

For example, a project I work on imports XML data from a "trusted" source, so in these kinds of models, there's no need for security. There are numerous models in the project like this. I'd essentially have to attr_accessible every last column, if the default were for all attributes to be blacklisted by default. This is somewhat arduous, and is somewhat of a maintainability headache, because if the schema changes later on, I have to add those new fields to the attr_accessible list.

If we're going to go with a blacklisted-by-default approach, is there some way for me to auto-whitelist every attribute without having to be so explicit as to type out each one?

@levhita
Copy link

levhita commented Mar 7, 2012

Probably a simple configuration should be enough blacklisted-by-default , a config to change it to whitelisted-by-default and the option to change it directly in the model at run time.

I don't see the problem really and rails has just maked a fool of itself...

PD: If you advertise you framework as "even an idiot can make a page in 30 minutes" you will end up with a lot of idiots writing pages in 30 minutes...

@csuhta
Copy link

csuhta commented Mar 7, 2012

06a3a8a

@RangelReale
Copy link

To me it seems obvious from the start that mass assignment should be disabled by default.

Otherwise, you add a field on a table, and you have to remember to block it on the model? If the field would be user-assignable, then of course a form would have to be modified to add an input, and at THIS time, you would modify the model, because it is related to the same change.

Yii framework in PHP which is very similar to Rails, blocks alls fields by default since the beginning.

If this is common practice in the Rails community, I would never trust a Rails application. This is as bad as SQL injection.

@Alamoz
Copy link

Alamoz commented Mar 7, 2012

Just received an email from GitHub, perhaps related to this issue:

"A security vulnerability was recently discovered that made it possible for an attacker to add new SSH keys to arbitrary GitHub user accounts. This would have provided an attacker with clone/pull access to repositories with read permissions, and clone/pull/push access to repositories with write permissions. As of 5:53 PM UTC on Sunday, March 4th the vulnerability no longer exists."

@norv
Copy link

norv commented Mar 8, 2012

Thank you!

@ELeo I was going to point out the commit above - cjcsuhta already mentioned it.
Reversing the switch is possible, for one's particular application.

The possibility of different sources for the data getting to the model, than http form input, and needing different treatment for them, tells me again that: authorization, filtering of the incoming data, and even validation needed for the data, are not really model issues. They may apply on the model, or be aware of it, but they cannot and should not be handled all at model level, otherwise you can't really have flexibility nor security as needed.
The handlers of those actions (i.e. controllers, forms, actions classes) should be responsible, directly or indirectly.

On the other hand, since in the case of web form data, there is an always-present need for request filtering, authorization, and validation, I find it more than reasonable to address these concerns at the level of a web framework.
If it's at the expense of convenience, then at the expense of convenience.
Security >> convenience.

At most, validation, from this list, may be disputable as to where it belongs, it may be the choice of a particular framework to handle it more tied to the model itself. I just don't see how could the rest be model matters, and in the same time have a both flexible and secure framework for development.
(Nevertheless, the commit pointed out above is IMO a step in the right direction, at least it makes sure the defaults will be saner).

Side note: I'd say admin is not really an exception in the sense of my question, since checking if the current user is an admin means an authorization check.

@brodock
Copy link

brodock commented Mar 13, 2012

@dburry
Copy link

dburry commented Mar 15, 2012

We developers using Rails should not have to write Merb or 0day the repo in order to get core Rails devs to stop saying "The emperor DOES SO have clothes!" Core devs, please change your attitude. We want newly-generated rails apps to have a setting in the application.rb that by-default turns off mass assignment, unless we reverse that setting in the application.rb or on a model by model basis. This way it's backwards compatible with existing apps too. Most of us want this. Stop making us resort to such tactics for anyone on the core team to listen.

@hron84
Copy link

hron84 commented Mar 16, 2012

@dburry you knocking on the open door. Please read back this issue, the future Rails versions will come with enabled enforcement.

@dburry
Copy link

dburry commented Mar 17, 2012

@hron84 indeed, for this issue... but will our attitudes change enough from this experience so that such tactics won't be necessary with other issues? I see the attitudes as the root cause that made this issue go so long ignored. And I realize this is more a social thing than a technical thing, so maybe this isn't the right forum for me to keep talking about it, sorry :P

@ghost
Copy link

ghost commented Jun 9, 2012

Isn't the question of whether mass assignment is enabled or disabled by default a bit of a red herring? I've just posted a question to this effect at SO, if anyone's interested.

@homakov
Copy link
Contributor Author

homakov commented Jun 9, 2012

@sampablokuper Answer is No. User is able to update HIS public key record AND he updates it with new user_id. code:

pk=current_user.public_keys.find(params[:id])
pk.update_attributes! params[:public_key]

@ghost
Copy link

ghost commented Dec 29, 2018

@fny What the fuck, why are you on about babys, rape and pedophiles you sick fuck. oh my god people are so weird nowadays

@fny
Copy link
Contributor

fny commented Jan 9, 2019

@def14nt I was arguing in a convoluted metaphor that the default convention should protect newbie developers ("babies") from hackers ("pedophiles and rapists".)

Reading the comment now, I do agree it was in poor taste. The analogies were an attempt to make a dry concept humorous, and in retrospect I see can how the comment can be seen as inappropriate.

It's been six years since I made that comment, and I was a naive 21-year-old at the time, so please forgive me.

@ghost
Copy link

ghost commented Jan 9, 2019

@fny Yeah it's OK, just maybe not speak like that elsewhere or your account might be taken down 😉

@rails rails locked as resolved and limited conversation to collaborators Jan 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests