Hacker News new | past | comments | ask | show | jobs | submit login
Is your Rails application safe? (railspikes.com)
56 points by jon_dahl on Sept 22, 2008 | hide | past | favorite | 33 comments

This is one of the major security design flaws in Rails, and this is a good article on the problem. What's worse is that the correct design is "folk wisdom" in the community (for instance, a 37Signals dev is never going to make this mistake), but the best-known Rails sample code is all vulnerable.

Every other web stack has this problem in one form or another; for instance, in J2EE, which is either the first or second best-secured web stack out there, you can bypass the best-known best-documented access controls by changing your HTTP verb from "GET" to "SUPERGET", "GETX", or "GIMME".

But Rails kind of went out of its way to win this particular vulnerability, and so now it's something you have to audit for on every project.

> but the best-known Rails sample code is all vulnerable.

Rails is moving so fast now that most sample code is very outdated. Take authentication for example, there's at least 5 different plugins, all favoured as the best way to authenticate in rails at one time or another. Totally hopeless for a newbie to dig out which is the solution to go with, or at least it's a few days trial and error, trying out tutorials here and there. I've done a few rails projects on and off and each time I come back there's tons of new stuff, which is really great, but I would personally like to have more batteries included or "blessed" official plugins because I don't spend all my time in rails land, and it's hard to keep track of all the new goodies.

Really interesting article.

not every rails application needs authentication.

I'm not sure what your point is, since every "serious" Rails app does need it, and even unserious web apps with "incidental" auth features can end up storing bank passwords.

i beg to differ. here are "serious" websites that doesn't do user authentication: - google.com (the original search engine) - techmeme.com - wikipedia (the original version) - tinyurl I could probably find dozens of examples, but those are just the ones I could find on my toolbar. And yes, those are not specifically rails application, but they do prove that there are web applications without user authentication

Ok, I'm not sure you've noticed the past few years of Intertubing, but Google has the second most coveted authentication token on the net. I'm not sure what a techmeme.com is, but I definitely know that Wikipedia has logins, and admin logins, and like 3 levels of authentication above that.

I guess is any app of value will eventually need authentication. Otherwise why are you using rails? Apache + fast CMS would probably be better ;)

The other security design flaw of Rails that drives me nuts is that it is vulnerable to XSS attacks by default because it takes positive action to strip HTML from user input.

I believe that security should be the default, automatic choice, and you should have to take an affirmative step to disable that when you need to.

Rails does get a lot right with automatic cross-site request forgery protection and nearly automatic SQL injection protection but the lack of automatic HTML stripping and the default accessible attributes thing makes me sad.

Rails is no more vulnerable to XSS than any other web stack; I pay for my dev team by looking at apps all day, and they all have this problem.

You're right that taking positive action to neutralize metacharacters is a weak design. There is a setting in Rails, IIRC, that automatically h() filters outputs, but it should be the default. Just as importantly, though, even if h() is the default, you still have to audit all the places where you deliberately allow HTML output, and every app has to do that somewhere.

I'm not disagreeing with you, but I am saying that there's a major difference between mass-assignment, which is a problem Rails seems to have blundered into, and XSS, which is endemic to web apps.

That is not true. Django and web2py both escape HTML output when variables are displayed, broadly neutralizing this attack by default.

Developers in those frameworks must explicitly say that they want to display HTML in order to have it displayed. Presumably, they have thought about this and also taken action to remove bad HTML from this output. When you have to make a choice to display it, it makes you think about this.

There is no setting in Rails to automatically escape HTML. There is Erubis, which is a replacement for Erb that can do it, and there are some XSS plugins that escape input on input or output.

I'm saying escaping HTML on output should be the default for the framework.

I don't think you read my comment carefully. The difference between XSS "susceptability" between Django and Rails is one setting (safeERB, sanitize, etc). Yes, it's a difference. I don't think it's a particularly meaningful one, because Django autoescape doesn't actually stop XSS attacks, just the stupid ones.

There's a big religious debate inside the Rails community about autoescaping, which ran aground on the point that autoescape doesn't actually stop XSS. I agree with Rails, but concede that reasonable people could think otherwise.

It's hard to see how reasonable, well-informed people could think that mass-assignment was a good default feature.

OK, I agree with you that escaping by default only stops some attacks, and if the developer's response is to simply disable the escaping every time they need to display HTML it's not going to be much help.

However, I would argue that nearly every Rails app does have susceptibility to these "trivial" attacks because Rails does not force you to think about when you want to display HTML.

I also agree that people disagree on this. But I still feel the default should always be the secure one.

I don't think that's a crazy point of view. I'm ambivalent. You have easier, better ways to win security arguments about Rails than the lack of one relatively trivial default setting, though.

Can you give an example of an XSS attack that isn't prevented by auto-escaping (other than the UTF-7 one)?

All the attacks that occur in the outputs that you can't auto-escape, because they need to contain HTML by design.

It's not the "escape" that's the problem, it's the "auto".

Gotcha, the HTML sanitization problem. Django doesn't even attempt to solve that, which is probably a bad idea since it inevitably means people will use the various insecure libraries that are out there on the 'net. The trouble is it's /really hard/ to do right - I'd be uncomfortable shipping a sanitizer with Django because I'd worry that some weird undocumented IE parsing error would allow nasty code to slip through and result in a 0-day exploit against the entire framework.

People tend to oversimplify this down to "how to accept HTML in user input", but the problem is more general: lots of applications have two-stage rendering sequences, where data is accepted and stored as HTML, and rendered later.

In the case of web2py there is an object called XML, text passed to the view inside XML is not escaped otherwise it is. You can also do XML(text,sanitize=True) and you can specify which tags and with tag attributes should be allowed. The sanitizer that ships with web2py is the one developed by Josh Goldfoot and posted on activestate, recipe 496942.

I would actually say Django has a very poor HTML escaping facility. (Though, better than Rails, for what that's worth.)

I feel like I've used template languages that actually understand XML (or HTML) and thus have context sensitive escapes. It was probably written in something gross like Java, so URLs were output verbatim in links, while say instances of "/search?q=" + someVariable would url escape someVariable in links, and html escaped in tag contents.

I'm very skeptical about the value of stripping HTML from user input - what if your site is a forum where people discuss HTML? Or a site where people might want to talk about the <head> conference? http://www.headconference.com/

Stripping HTML on input also means you lose the ability to audit attempted attacks against your site.

A much smarter strategy in my opinion is to escape everything on output. That way it doesn't matter what people submit to your site - unless you explicitly say "this is safe to display unescaped" in your templates harmful characters will be neutralised.

Yup. I am suggesting escaping HTML on output (of user input) by default -- making h() the default, essentially.

(Incidentally, the reason why this will probably never happen is that it would break all of Rails' HTML helpers.)

There are several frameworks that do this already. Django even broke existing apps to make such a change.

This only stops the "hello world" attacks. Be careful not to give the impression that Django "solved" cross-site scripting; they clearly haven't.

Any suggestions for other things we could be doing?

white_list is a better option, imho

Look, it's fair to freak out, but it's also fair to point out that most people have known about this problem for a long time. I was scrubbing my hashes of unwanted keys as far back as when I wrote ma.gnolia.com in 2006.

And it was considered common knowledge back then. And most people don't fix it using attr_protected, so are we really sure that all those projects are vulnerable? A simple string search would not suffice.

Eric executed actual attacks against all the projects mentioned in the article. Most of them resulted in data corruption (e.g., making a page unviewable by setting a non-existent user ID) but on one he was able to change his order status to "paid".

Nobody's panicking, are they? My company chose Rails, and I'm mostly happy with it. I'm just calling a spade a spade.

It would be crazy to assign unfiltered params to your model objects/DB and I would be astonished if anyone did it - except I suppose inexperienced programmers following simplified example code. But if you code web apps without knowing roughly what you're doing, you're going to fall into security traps whatever framework you use.

Thanks go out to Eric for this article, which led (among other things) to fixing some important bugs in Insoshi. You can find my companion post on fixing Rails mass assignment problems here:


I never even knew about attr_protected / attr_accessible until I saw them being used in acts_as_authenticated.

Come to think of it, I learned a lot of good practices from AAA (and restful_authentication). attr_protected, salted/hashed passwords, and several other nifty tricks.

The problem isn't that rails is insecure (it is, but that isn't the point.) The problem is that rails by definition attracts developers who don't really like developing.

If the entire USP of your platform is that it cuts corners you'll see it put to use by people who either don't care or don't know any better.

I'm not saying there aren't good rails developers, but in my experience they are a minority.

Doesn't protect_from_forgery solves this? I'm pretty sure when I tried using curl the other day I was getting a message error message because the form was missing the secret.

That's to protect against cross site request forgery. This is setting sensitive attributes via post. If a post has the authentication token, it can still set sensitive attributes.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact