There's a Rails design pattern where you send an AJAX request to your controller, and it returns a bunch of Javascript that gets eval'd by the page you're on. This is used for say, infinite scroll. You'll click the 'load more' button, the server will render a partial containing your new records, and return something like:
$('#itemsList').append('<li>My new item</li><li>another new item</li>')
So guess what? This is susceptible to XSS-ish attacks.
Let's imagine that instead of a simple list item, this request was returning some sensitive information or a CSRF token. (Egor points to various examples of this.) Normally, executing a GET request from an unauthorized domain will not be allowed because of CORS. This is what prevents me from instructing the users of my website to make an AJAX request to gmail.com, allowing me to see all of their emails.
But "wait", you say, "the content type of this request is application/javascript."
So open developer tools and type this: (using jQuery for conciseness)
Isn't this solved by just requiring the CSRF token on any JS get requests? (In fact, isn't this just a cross-site request forgery with a different verb than we're used to?)
I know it's only generally checked on posts, but turning it on for any xhr calls seems like it would solve any potential data leakage.
basic/digest auth doesn't solve this if you've recently used the target site any more than cookies do, because your browser caches the authentication for some time and won't ask you again.
Also, very few web applications use basic auth, most use cookies.
He also linked to a post from six years ago[0] which had a good solution the problem at the time: pass data, not code (and process this data on the client side).
I just managed to confirm that a similar attack is possible against sites using DjangoRestFramework. I won't publish it as the site I used to test against is currently working on patching the vulnerability out.
If you believe you've found a security issue in Django REST framework I suggest raising emailing the security contact as listed here: http://django-rest-framework.org/#security
Having said that, it's worth pointing out that Django REST framework does not return JSONP by default, and although it does for historical reasons include a JSONP renderer, the documentation recommends the use of CORS instead.
Except inasmuch as you're using a framework that allows any JSON data to also be requested as JSONP. The very first site I checked on this (which uses DjangoRestFramework) I was able to access email addresses from an attack page. This is purely due to DRF handling the JSONP for you without the devs really being aware of what was going on.
Quick question, how are these files bypassing the authentication before_filter? I mean, a progress.js (Dont know what that is) probably belongs to a project, and therefore will hit that controller first, and the controller should make sure the file is not shipped to anyone but the authorized user. Am I missing something here?
The attack scenario here is a request from an authorized browser which is run by a hostile page on a different website (e.g., due to phishing attack). And since the request comes from an authorized browser, the cookies typically checked by an authentication before_filter will be present. In detail:
1) the attacker knows of an authorized user, already logged in (most likely). So, any request sent by that browser to the target site will pass authentication checks.
2) they trick that user into loading a web page under their control by some means (e.g., phishing attack).
3) that web page, under their control, includes a <script> tag with a src attribute referencing target .rjs. It loads the target .rjs Javascript into a gimmicked environment in which the private data is captured. Since the web page is under the attacker's control, same-origin rules allow that captured private data to be sent back to the attacker directly.
To counter this, the target site needs some way to verify that its own Javascript is making the request, not some hostile web site. Thus the talk of CSRF tokens and checking .xhr?. (The latter counts on the browser's same-origin rules to prevent an AJAX request from the hostile web site --- the same-origin rule blocks that, but in most current browsers, there's no way to prevent the attacker from tossing in a <script> tag.)
what I don't understand is: isn't the same information (say, a CSRF token) returned if the response is html (the AHAH pattern)? What makes rjs different?
The key to this exploit is that JavaScript that is designed to be fetched via XHR and then eval()d can be used by another site to "steal" data by linking to it in a script tag.
An obvious fix would be to send that JavaScript with a content type other than "application/javascript" or "text/javascript"... but considering some browsers execute JS sent with the wrong content type I wouldn't trust that to actually work.
A technique that definitely does work is to have the first line of the JS prevent execution in some way. When the code is fetched by XHR this line can be stripped out before calling eval(). This cannot be done by a malicious page that includes the code using a script tag.
I seem to remember seeing Google use "while(true);" for this exact purpose in the past.
>An obvious fix would be to send that JavaScript with a content type other than "application/javascript" or "text/javascript"... but considering some browsers execute JS sent with the wrong content type I wouldn't trust that to actually work.
I believe all browsers will try to execute. Sniffers
>I seem to remember seeing Google use "while(true);" for this exact purpose in the past.
Not this exact, but relevant. That one was JSON-leaking via redefining Array prototype.
But would this same solution work with this vulnerability? The server sets the first line of the response js to `while (true) ;` and the js handler that does the eval just deletes the first line like so:
The exploit occurs when an evil third party site links to your script from a script tag - they can't modify the returned code before it is evaluated. Your own code running on the same domain (which fetches the code using XHR) can make the modification and hence execute the script.
I find it interesting that DHH dismissed this out-of-hand given Egor's history with Rails. In his tweets he highlights some pretty large Rails projects that impact a lot of users/sites.
It's clear that the author is frustrated though. Maybe it's just me but I feel like a framework's core team should treat security vulnerabilities (or even common design patterns that lead to them) as stop-the-world events until they are resolved.
It's not up to the issue reporter to come up with a fix that satisfies the core team.
I suppose this won't work for RJS, but for other similar JSONP vulnerabilities the correct approach is probably to strip out authentication from JSONP requests -- this could be done at the middleware layer or where authentication happens. Now JSONP would only leak data that you'd give to any other anonymous user.
JSON and clientside templating, folks. It's a wee bit more work, but it's a lot more pure, and that purity pays off in multiple regards, including security.
I have existing templates that exist server side. I don't want to recreate them client side. Rails JS responses to the rescue.. It's quite elegant and works well with the existing Rails workflow.
Client side templating would be useful as a default feature in Rails, the framework already makes great use of partials. But to get client side templating to work you have to choose one of the new javascript frameworks and duplicate your partials into its' related js directory.
The Rails way has you transmitting html. That isn't great in cases where there may be thousands of entries, in this case the Rails way isn't the best.
I've been tinkering with a setup internally that uses a modified Hamlbars with Draper to generate serverside HTML and clientside templates from the same source template. It needs a bit of fleshing out, but the idea works.
This is sad, and this is being flagged too, so fast, seriously people? This is a genuine security issue which needs to be alerted to everyone out there. And Homakov has also gone so as far as to make some pull requests and he has been asked to stop (WTF?!). Really I see this more of like arrogance from the project maintainers' end.
I'm thankful that I don't depend/use on any of these features for my project though.
Switching to jQuery made life so much easier. Once a feature is completed, I feel happy. With RJS, I was just glad that it was over. And, no, I do not want to touch it again :-)
I can say is that with jQuery you just get the JSON or HTML you need. And then you either process it or put it where its supposed to go. The server just creates the JSON or HTML.
An RJS file is a template for generating javascript on the server side with your data. Writing code with a tempting language is more of a pain than just writing that processes JSON or moves HTML.
I get redirected to a login page when attempting to access a spree /admin/products/new.js ... where's the leak or did I need to first have access to the users session?
you have to be logged in for the request to work right?. if you are not logged in / don't have the cookie / session it won't work. so whats the problem? when your logged in you see all that stuff anyway why does it matter which flavor its in?
as long as your session is secured how will this work?
an easy fix, I think, would be to always wrap the returned code in a function that would check if the domain matches a predefined whitelist and just return out of the function if it doesn't. This can be very easily automated.
If you don't NEED to support GET requests with JSONP / JS to be evaled, then don't.
If you do need to, then the trick is to prefix what the script returns with something that will always throw a syntax error or discontinue evaluation, the idea being that your script (same domain) can cut off the prefix and then evaluate the JS, but the attacker relying on a <script> tag cannot.
I was pretty sure that if its comming from a different domain that this is not the case.
For example:
If we have legitsite.com/json-only-with-sensitive-data.js and we load that by adding a script tag to attackersite.com then attackersite.com cannot get the contents of what the legit site returned.
The problem is when you either have JSONP (for which the attacker site could add a callback in the request to execute) or if you return js code that is supposed to run like in this blog post where RoR returns code like this:
$('#someid').html('sensitive data');
...
In this case it's just as bad as having JSONP because the attacker site can create a <div id='someid'></div> then add the script tag then onLoad of the script tag inspect the contents of the div.
Can you show an example how? Not 100% sure, but I believe all major browsers will block the direct access to the content of an external JS file from a different domain? You get just an empty string if accessing it from the DOM (at least in chrome)
RJS was removed in Rails 3.1, IIRC. It hasn't been considered best practice for some time.
That said, an article has been on the front page all day about not using shiny new tech by an author that recommends the use of Rails 2.3 and RJS today, soooo...
There's a Rails design pattern where you send an AJAX request to your controller, and it returns a bunch of Javascript that gets eval'd by the page you're on. This is used for say, infinite scroll. You'll click the 'load more' button, the server will render a partial containing your new records, and return something like:
So guess what? This is susceptible to XSS-ish attacks.Let's imagine that instead of a simple list item, this request was returning some sensitive information or a CSRF token. (Egor points to various examples of this.) Normally, executing a GET request from an unauthorized domain will not be allowed because of CORS. This is what prevents me from instructing the users of my website to make an AJAX request to gmail.com, allowing me to see all of their emails.
But "wait", you say, "the content type of this request is application/javascript."
So open developer tools and type this: (using jQuery for conciseness)
And boom, you have a bunch of sensitive information about your Basecamp project, accessible via JSONP request from any domain.