On the recent Rails CVEs
The case-insensitive filesystem hole is pretty much a non-issue for me (and almost everybody else who deploys on Linux) because all of the mainstream filesystems actually used on servers are case-sensitive.
The SQL-injection via "limit" clauses is not exploitable in my app seeing as there are no places where a user-supplied limit clause is provided.
This leaves only the CSRF hole, and it's an issue that makes me rather sad.
If you're not familiar with CSRF, the Wikipedia article on the subject is a good starting point.
In short, the attacker tries to trick the victim into submitting a request to a third-party site where the victim is currently logged in. For example, let's imagine that our attacker, Mallory, somehow makes the victim visit a page with a bogus
<img> tag like this:
If the user is logged in at "victim-bank.example.com" (for example, via a cookie), then the request will cause $500 to be transferred into Mallory's account, even though the victim did not intentionally visit his or her bank's website.
Note how the user doesn't need to actively click on the link in order to trigger the request; the mere loading of the
<img> tag will be enough.
Note also how this is a "blind" attack. The attacker never sees the result of the request, but he nevertheless seeks to trigger some outcome that is desirable to him. In this case that outcome is transferring money to his account, but it could just as well be destroying data, or resetting a password, or any number of other things.
Rails provides built-in CSRF forgery for all POST requests made via forms. A one-time token is embedded into all forms using a hidden input, and any POST requests made without that token are rejected. The attacker can try to submit such a request, but it will fail, because the token will not be included.
This leaves GET requests like the one in the example above. The fix here is to follow the HTTP spec and ensure that all GET requests are side-effect free. Any action which has side-effects (like transferring money, resetting passwords, or deleting/mutating data) should instead be performable via POST only. The attacker can submit any GET requests he likes, but without any consequences.
Finally, we can limit the window of opportunity for such attacks by reducing the amount of time users stay logged in via cookies, or requiring them to re-authenticate before destructive or dangererous operations.
Unfortunately the official advisory doesn't go into detail about exactly how an attack would work but it does say this:
Certain combinations of browser plugins and HTTP redirects can be used to trick the user's browser into making cross-domain requests which include arbitrary HTTP headers specified by the attacker. An attacker can utilise this to spoof ajax and API requests and bypass the built in CSRF protection and successfully attack an application.
Looking at the actual commit which purports to close this attack vector (66ce3843), we see that the "certain combinations of browser plugins" probably refer to our good friend, Flash:
Unfortunately the previous method of browser detection and XHR whitelisting is unable to prevent requests issued from some Flash animations and Java applets. To ease the work required to include the CSRF token in ajax requests rails now supports providing the token in a custom http header:X-CSRF-Token: ...
So this looks to be yet another example of Flash providing the means of executing an attack. Seriously, Flash has to be the suckiest piece of technology in the history of the web.
Update 1: More comments from Michael Koziarski (author of the advisory):
We've been deliberately vague in the description of the exploit so as not to provide ready-made exploit kits to attack other frameworks and rails applications which haven't been upgraded. We've been contacted since by a browser vendor and a few other frameworks requesting details. I'm sure the full details of how this can be exploited are already available in some circles but for now we're not going to make that job any easier.
I don't think providing any more details here will help anyone, it affects your users and is easily exploitable.
Why this makes me sad
This change means that we can no longer rely on the browser's "same origin" policy to keep AJAX requests safe. In turn, all pages with AJAX POST requests on them now require a unique token to be embedded within them, much like the existing CSRF protection in Rails provided for forms.
This is sad because it means it is no longer trivial to page-cache pages with AJAX POST requests on them, because if you do so you will end up leaking stale CSRF protection tokens into your cached pages, not just leaking information but also preventing the requests from working.
Page-caching is my favorite kind of caching in Rails, because it allows you to bypass the entire Rails stack and serve directly from your front-end of choice (eg. nginx). This is insanely fast (or rather was insanely fast); if your application server can do 100 requests/second when hitting the full Rails stack, it can probably easily handle 1,000 requests/second when serving up static files directly via nginx.
You can sometimes use AJAX to side-step this issue, depending on the content you need to display. For example, just say you need to show one kind of comment form to logged-in users and another to anonymous visitors; you can pull down such a form via AJAX on demand, but only when the user actually clicks on the "add a comment" link. In this way most of your requests will be lightning fast, and the minority of users which actually want to comment will only trigger another, slower request once they actually hit the link.
But with this change we now have a whole new class of pages that can't be page-cached (pages with AJAX POST requests on them). It sucks that one of the most scalable caching methods is becoming less and less applicable.
And sadly, if this is a security bug in Flash and/or Java, it's sad that we framework users are now saddled with this uncomfortable, functionality-impairing workaround, rather than the hole being fixed upstream where it really lies (in Flash/Java).
Add a comment
Comments are now closed for this snippet.