Remove :js responder

Not that would require to configure the application host manually like:

Nevermind, it is not safe since the information about who is requesting is a header that can be changed.

What about prefixing while(1) on the beginning of js responses with rack middleware, and then stripping them out client side?

This is the solution used by Facebook and Google.

This might work out, but damnit, isn’t everyone agreed here that “returning JS” is 2008 style?

Rails should move on, to API-like servers and single page apps, not necessarily breaking old school tools, but such a dinosaur should be considered as a bad & insecure practise. Why patch it then at all?

Related post

Rails should move on, to API-like servers and single page apps, not necessarily breaking old school tools, but such a dinosaur should be considered as a bad & insecure practise. Why patch it then at all?
Single page apps are not everyone’s cup of tea. Maybe I am old school but I like to keep on the server side everything I can because I have more control there.

Would a solution be to prevent rails apps from serving “non static” JS from GET requests. Assets served from the asset pipeline would be allowed. JS returned via controllers/views would need explicitly defined permission to be served via a GET request.

That’s still OK if it’s public data. (Obviously anything with a CSRF token in it isn’t.)

It’d be nice if it was more explicit though, as the vulnerable cases are not obvious.

On one hand if you explain/reach people with security concern of JS templates they will most likely rewrite it to JSON-style.
On the other hand if they blindly update their rails/don’t read news - the only way to reach them is Deprecation warning. Or maybe just WARNING?

What I actually want is to make people understand and check if they have this problem. Deprecation is on of the means to do it.

No, it isn't. Deprecation means we intend to remove that feature in the next major release, which we don't want to do. Single-page apps maybe the latest hotness but that doesn't invalidate the previous two decades of web development. We didn't remove forms when CSRF attacks were developed, we developed protections against them. This is exactly the same scenario - we need to develop useful, easy to use protections that we can enable by default.

Egor, can you clarify one point for me - you mention that JS templates allow stealing of the CRSF token if they return a form, but surely that would true even for HTML templates?


The mechanics of the vulnerability, which Egor’s blog does not really communicate effectively, are, as I understand them:

an evil site includes a tag referencing a GETable .js.erb url from a good site. If the user is logged into the good site via cookies and has 3rd party cookies enabled, the request will succeed and return js possibly containing html with data indented to be private between the user and the good site. This js will execute in the js environment under the control of the evil site (it can override any function, method), handing the evil site the intended to be private html.

This attack is not possible with non js content loaded by XHR or iframes, as the browser enforces cross-domain restrictions for both, and evil site will not be able to get at good site’s content.

If the operators of EvilSite have gone to such lengths to contrive forms
and overridden JS methods to potentially steal a tiny bit of possibly
private HTML and data, could they not take the next small step and use a
browser that *does not* enforce cross-domain restrictions on XHR? (or
frankly, write their hacks with wget or curl)

The idea is not that the evil site operators will access their own site themselves, but that they will get legitimate users of your site to visit their evil site (and thereby steal the legitimate users’ private data). So, for their ploy to be successful, it needs to work in standard browsers that ordinary users will be using.


If the security concern is only about CSRF, what about not rendering CSRF token in templates at all?

I mean, UJS may solve this problem appending the CRSF token from meta tag.

If that’s not elegant since it will require javascript, even for static forms, we may do that only for .js.erb views.

Basically, if a form is rendered through a js view, do not render the CSRF token.

Homakov, that would fix the security concern without removing the .js.erb views?


Gabriel Sobrinho

I'm afraid the CSRF token is not the only private data that could be sent in the template.

I'm actually surprised so many applications seem to use RJS, including some applications I do use, like Redmine or GitLab.

Maybe I should create the habit of using an anonymous session for regular browsing to avoid getting my private data stolen from applications using RJS in Rails... :frowning:

I really think RJS should be moved to a gem and not included by default in Rails and stop promoting its usage...


The mechanics of the vulnerability, which Egor’s blog does not really communicate effectively
what’s not effective in my post? As I said attack works as JSONP hijacking. So everything is totally obvious, isn’t it.

Apparently many readers have no clue how this attack works, and people keep asking the same questions. Thanks to people who clarified it in more details than i did.

All we can do is to add is-.xhr? protection or Warning (not necessarily Deprecation). There are no other sane way to mitigate it.

I see, extracting it as a gem fix the problem, at least for the rails itself, which is what we want.

The extracted gem must explicitly explain the security concerns on the top of README, regardless of people usually don’t read it.

Rails already did that for other features, for other reasons but the same idea applies, deprecate the usage on rails itself but allow who explicitly wants/needs to use.

Time to pull request?


Gabriel Sobrinho

I think we should rather try to find a way to make this secure. What would be a sane default? Only respond to js format is the request is xhr?
To be honest I read Egor’s post but still not sure how this exploit would work. I will look at his examples when I got some free time and hopefully that will help to understand it more.

I believe the reason why it's hard for us to understand how the exploit works is because it's pretty hard to find the documentation for RJS itself and specially how it works...

I'm assuming, it works like JSONP, since Egor must know what he is talking about.

In this case, it won't require a XHR request and won't send any nonce (like the XSRF token) for GET requests, and will work with a regular inline script tag.

In that case, the trick of prepending a "while(1)" would probably fix this particular issue because it wouldn't allow the code to be evaluated, no matter whether you have changed the JS global context or not.

Another way of fixing it if I understood correctly would be to require all RJS requests to happen with XHR since they are subject to same-origin browser's policy.

Yet another way to fix it would be to always require a nonce even for GET requests to XHR templates requests.

Am I missing something?