Remove :js responder

2,129 views
Skip to first unread message

Egor Homakov

unread,
Nov 28, 2013, 3:41:37 AM11/28/13
to rubyonra...@googlegroups.com
https://github.com/rails/rails/issues/12374#issuecomment-29446761

Here in discussion I proposed to deprecate JS responder because this technique is insecure and not pragmatic way to transfer data.

i find this bug very often so i know what i'm talking about. With it attacker can steal user data and authenticity_token if templates with form were leaked too.



Aaron Patterson

unread,
Nov 28, 2013, 3:49:40 PM11/28/13
to rubyonra...@googlegroups.com
Removing it seems fine to me, but I suppose we should deprecate it
first. Don't people need to specifically say "render js: whatever"?

I think 100% of "render js:" cases can be implemented using JSON. But
maybe I am wrong.

--
Aaron Patterson
http://tenderlovemaking.com/

Steve Schwartz

unread,
Nov 28, 2013, 4:02:14 PM11/28/13
to rubyonra...@googlegroups.com

A lot of people use the js responder with ujs, but there are often bugs with how jQuery handles the automatic code execution of js ajax responses, so I agree, it's something I wouldn't mind deprecating.

One reason people tend to use js responders is to use js.erb to embed values from ruby into the returned js, but I think a better way to do this is to use json and HTML data attributes to embed values when necessary.

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/groups/opt_out.

Godfrey Chan

unread,
Nov 28, 2013, 4:42:26 PM11/28/13
to rubyonra...@googlegroups.com
I am not against removing this (I personally don't have use for this anymore), but the reason behind the removal this seems unclear to me.

If it's just "let's remove this because no one uses this anymore" (and if that's true), or "Rails want to discourage this type of architecture in favour of moving these type of processing to the client", then fine. But I am not sure if I fully understand the security angle here.

Specifically, in Egor's blog post:


1. "Broken concept & architecture. This feels as weird as the client-side sending code that is going to be evaled on the server-side... wait... Rails programmers had similar thing for a while :/"

But in the context of a web application, by definition you are always sending JavaScript code from your server to be eval()-ed in the client (browser) :) Whether the code is in /assets/javascript or in your controller, there's really no difference here.

So I'll have to assume you are talking about eval()-ing code that contains user-supplied data that's bad. Which is fair enough, considering there's usually a safer way to do this (encoding the user-supplied data as JSON first).

But as Andrew pointed out on the Github issue, there might be legitimate use cases for using this to return code that does NOT contain user-supplied data. I think those use cases needs to be understood properly (and we need to investigate/suggest alternatives for those cases) to ensure we are not removing a useful feature just because it's subject to abuse.


2. "Escaping user content and putting it into Javascript can be more painful and having more pitfalls then normal HTML escaping. ... There can be more special characters and XSS you should care about."

Is this not already adequately addressed by the escaping helpers Rails provide? If so, regardless of whether we remove the js responder, those issues should be reported and fixed in Rails itself. I just opened https://github.com/rails/rails/pull/13073 yesterday to patch json_escape to make it useful again (please provide feedback), and I plan to submit another PR to improve the docs on escape_javascript to make the intended use cases more clear.

If it is already possible to correctly escape input for use in the JS responder, just that it's difficult to do it correctly, the problem might not lie in the JS responder pre-se and we should take this opportunity to address them.

3. "JSONP-like data leaking. If app accepts GET request and responds with Javascript containing private data in it attacker can use fake JS functions to leak data from the response."

I assume you are talking about CSRF attacks where a page on a third-party domain can make requests to your site on your user's behalf. But like you said, this problem also exists in JSONP. In fact, it's probably much easier to exploit this in JSONP (because you can control the name of the callback) than a JS response that updates a certain part of the page. So I'm not sure if this is really JS responder's fault. And like Andrew said, in his use case there's nothing to leak.

Maybe I am not understanding this point correctly. If you can provide some examples based on the apps you see in the field it will be very helpful.

4. "UPD as pointed out on HN evaling response will mess with your global namespace and there is no way to jump into closure you created request in.."

(I suppose this is not related to security.)

If you are doing a lot you should definitely architect your client-side JavaScript properly, that I agree 100% (I mainly write full-blown single page apps these day for my day job, so believe me when I say we are in agreement :). But in cases where you are delivering a small, simple payload to update a small part of the page, I don't know if that's super evil and should be strongly discouraged.

I don't do this, so I'm not really in a good position to defend it. Again, maybe it's just a case of "no one uses this anymore so it's not worth supporting", and that's fine by me. But you brought up security, so I want to make sure we address your concerns properly.

Godfrey

Alex

unread,
Nov 28, 2013, 4:45:28 PM11/28/13
to rubyonra...@googlegroups.com, tende...@ruby-lang.org
I believe the primary use case for js responder is remote forms, where you are not doing the request yourself, so a js response is the simplest and most natural approach there.  This is not inherently insecure so should not be deprecated just because it does not feel good to some.

Steve Schwartz

unread,
Nov 28, 2013, 4:54:01 PM11/28/13
to rubyonra...@googlegroups.com, tende...@ruby-lang.org
A lot of the proposed issues aren't really too bad IMO, they're mainly just related to one developer's preferences and taste over another. I personally don't think returning JS from an ajax call is much (if any) worse than returning HTML. It's all just your server rendering some markup/code for the browser to interpret.

However, from the UJS side, the main issue with using JS as the response, is that the user has no control over the exact context of the automatic eval being done by jQuery, or exactly when during the callback sequence the code is eval'd. This actually caused code to break at one point due to a jQuery update, which would cause issues if your returned JS replaced the element that triggered the UJS remote (ajax) call. See:


That being said, I'm not necessarily in the "we should deprecate this" camp. I'm somewhat indifferent. Deprecation means one less thing to maintain (and would allow me to delete all of one line in UJS).

-- Steve Schwartz

--

Maurizio De Santis

unread,
Nov 28, 2013, 8:34:18 PM11/28/13
to rubyonra...@googlegroups.com
About the HTML rendering use case, I would point out that it could be adequately solved using render_to_string, and IMHO more elegantly, since the related JS could be included in the assets, and so organized toghether with the other JS source.

I can't think about use cases where RJS is preferable over render :json, does anyone do?

Scott Johnson

unread,
Nov 29, 2013, 12:57:18 AM11/29/13
to rubyonra...@googlegroups.com
I am -1 for deprecating the js responders 

Personally I find using the js.erb templates much easier, more DRY and more expressive then returning a HTML string as a JSON element.  Any JavaScript in the js.erb should be specific to initializing the rendered HTML and would exist in the event handler of the AJAX call anyways, so having it in the assets seems of little benefit.  All the reusable code should live in the assets either way.

It does not seem like removing it provides any benefit to the people who would prefer to use all JSON.  So, other than forcing a preference on the community, why deprecate it?


Scott Johnson
Message has been deleted

Egor Homakov

unread,
Nov 29, 2013, 5:55:25 AM11/29/13
to rubyonra...@googlegroups.com
Focus on security concern. I repeat  - any GET accessible .js.erb IS a vulnerability because leaks all data it has inside. 
All "i use it because it's fast/convenient" means nothing if it's a vulnerable technique. IMO  

Egor Homakov

unread,
Nov 29, 2013, 5:56:58 AM11/29/13
to rubyonra...@googlegroups.com
Normally can be exploited this way:

function $(){ return {html: function(){ console.log('LEAKED',arguments);}}};
document.write('<script src="http://SITE/objects.js"></script>')

On Thursday, November 28, 2013 3:41:37 PM UTC+7, Egor Homakov wrote:

DHH

unread,
Nov 29, 2013, 10:11:06 AM11/29/13
to rubyonra...@googlegroups.com
Not only are js.erb templates not deprecated, they are a great way to develop an application with Ajax responses where you can reuse your server-side templates. It's fine to explore ways to make this more secure by default, but the fundamental development style of returning JS as a response is both architecturally sound and an integral part of Rails. So that's not going anywhere.

So let's instead explore ways of having better escaping tools, warnings, or defaults that'll work with the wonders of RJS.

Egor Homakov

unread,
Nov 29, 2013, 10:27:15 AM11/29/13
to rubyonra...@googlegroups.com
@dhh as i mentioned above for GET request this will always be a security breach. So leaving it for POST only? Doesn't make sense.
Message has been deleted

Godfrey Chan

unread,
Nov 29, 2013, 7:43:51 PM11/29/13
to rubyonra...@googlegroups.com
Right, I agree that if used incorrectly this be a vector for CSRF attacks. But like you said, this is also a problem for JSONP, which is by far way more popular. So unless we also remove JSONP there problem you are complaining is still there, no?

Maybe we should improve the documentation to point out that you should not be using cookies to authenticate "private" JSONP/JS endpoints? Perhaps we can make it easier to do token-based authentication for API requests?

Because personally I don't see JSONP going away.

Godfrey


Godfrey Chan

unread,
Nov 29, 2013, 7:45:08 PM11/29/13
to rubyonra...@googlegroups.com

On Fri, Nov 29, 2013 at 7:27 AM, Egor Homakov <hom...@gmail.com> wrote:
@dhh as i mentioned above for GET request this will always be a security breach.

GET requests using Cookies for authentication. That returns non-public data.

Egor Homakov

unread,
Nov 29, 2013, 9:05:10 PM11/29/13
to rubyonra...@googlegroups.com
It's not very proper comparison. Nobody uses JSOPN w/o *a reason*. Autocomplete, as maximum. And security concern of jSONP is way more known than RJS bug (did you see articles on this topic but mine?)

>Maybe we should improve the documentation to point out that you should not be using cookies to authenticate "private" JSONP/JS endpoints
IMO, it should be fixed somewhere in code, not in documentation. People don't read it periodically. 
Not using cookies for JS responses breaks the idea of templating. For API people should use JSON(P). Why JS?

Egor Homakov

unread,
Nov 29, 2013, 9:05:47 PM11/29/13
to rubyonra...@googlegroups.com
>GET requests using Cookies for authentication. That returns non-public data.
most of the time this is the case. JS templates return whole forms with authenticity_token in it!

Godfrey Chan

unread,
Nov 29, 2013, 9:45:38 PM11/29/13
to rubyonra...@googlegroups.com

Thanks for the explanation. I now better understand the point you made in your original argument and the security concern you raised.

> >GET requests using Cookies for authentication. That returns non-public data.
> most of the time this is the case. JS templates return whole forms with authenticity_token in it!

My point here is not that "JS endpoints that uses cookie + return private data" isn't a common misuse, that I'm sure you are more qualified to give an opinion than I do, based on the apps you work on. My point is I don't think that this is inherently flawed and fundamentally impossible to fix (and hence we *must* remove it). Not using cookies for auth is one possible way to fix, but as you pointed out...

Not using cookies for JS responses breaks the idea of templating. For API people should use JSON(P). Why JS?

And I think you are quite right. Having to deal with alternative authentication scheme that does not rely (only) on cookies, on both the client and the server side, probably cancels out a lot of the benefits of using JS responses (simplicity etc).

However, I think there are probably something else that Rails can do to fix the security concerns you have. For example, we can automatically wrap all JS responses with something like... 

if(<%= an_array_of_whitelisted_hosts %>.indexOf(window.location.host) > 0){
  // ... the actual response goes here ...
}

What do you think about this? It seems like this would be quite easy for Rails to implement. We can turn this on by default with the list defaults to request.host and provide a way to opt out. Would that address your concerns?

Again, I personally don't use this feature, so I don't have a strong opinion on whether it should be kept in core. I'm just focusing on the security concerns you raised and see what we can do to make Rails better. And I suspect there are ways to make this feature more secure by default without removing it.

Godfrey

Egor Homakov

unread,
Nov 29, 2013, 10:54:35 PM11/29/13
to rubyonra...@googlegroups.com
location.__defineGetter__('host',function(){return 'google.com'})
undefined
location.host

Courtenay

unread,
Nov 30, 2013, 4:49:42 AM11/30/13
to Ruby on Rails: Core
Since POSTs already require a csrf token, the rjs calls should do the same thing. Make them require the auth token in the url, or even better, have rails generate a hash of the auth token and the url and compare on the server. Thus you can't call any rjs without using rails helpers to generate the correct url and matching token, and the new token would not be leaking the original csrf token via url or referer. It's effectively using methods similar to how we build APIs with hmac signing of the request, but in a lightweight form and only in the background for RJS (or whatever else you want to protect, I suppose). This seems a little out of the scope of the core rails project, though?

Gabriel Sobrinho

unread,
Nov 30, 2013, 9:32:30 AM11/30/13
to rubyonra...@googlegroups.com
Homakov,

What Godfrey suggest may be implemented on the server side instead of client side without being hacked so easily?

We may raise an exception by default when the host is not allowed, assuming that request.host is trustable.

Gabriel Sobrinho

unread,
Nov 30, 2013, 9:33:26 AM11/30/13
to rubyonra...@googlegroups.com
Not that would require to configure the application host manually like:

Rails.application.config.host = 'example.com'

Rails.application.config.hosts = %w(example.com example.com.br)

Gabriel Sobrinho

unread,
Nov 30, 2013, 9:42:53 AM11/30/13
to rubyonra...@googlegroups.com
Nevermind, it is not safe since the information about who is requesting is a header that can be changed.

Brian D. Armstrong

unread,
Nov 30, 2013, 2:19:09 PM11/30/13
to rubyonra...@googlegroups.com
What about prefixing while(1) on the beginning of js responses with rack middleware, and then stripping them out client side?

http://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses

This is the solution used by Facebook and Google.

http://blag.7tonlnu.pl/blog/2012/09/27/json-hijacking-in-rails/

Egor Homakov

unread,
Nov 30, 2013, 11:27:21 PM11/30/13
to rubyonra...@googlegroups.com
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?

Egor Homakov

unread,
Dec 1, 2013, 1:32:51 AM12/1/13
to rubyonra...@googlegroups.com

Greg Molnar

unread,
Dec 1, 2013, 7:58:28 AM12/1/13
to rubyonra...@googlegroups.com
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. 

Anthony Richardson

unread,
Dec 1, 2013, 7:59:27 PM12/1/13
to rubyonra...@googlegroups.com
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.


--

Will Bryant

unread,
Dec 1, 2013, 8:40:52 PM12/1/13
to rubyonra...@googlegroups.com
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.

Egor Homakov

unread,
Dec 1, 2013, 10:42:16 PM12/1/13
to rubyonra...@googlegroups.com
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.

Andrew White

unread,
Dec 2, 2013, 3:41:07 AM12/2/13
to rubyonra...@googlegroups.com

On 2 Dec 2013, at 03:42, Egor Homakov <hom...@gmail.com> wrote:

> 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?


Andrew

Alex

unread,
Dec 2, 2013, 4:09:02 AM12/2/13
to rubyonra...@googlegroups.com
The mechanics of the vulnerability, which Egor's blog does not really communicate effectively, are, as I understand them:

an evil site includes a <script> 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.

Michael Pavling

unread,
Dec 2, 2013, 4:32:46 AM12/2/13
to rubyonra...@googlegroups.com
On 2 December 2013 09:09, Alex <alxt...@gmail.com> wrote:
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)

Chris Mear

unread,
Dec 2, 2013, 4:37:03 AM12/2/13
to rubyonra...@googlegroups.com
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.

Chris

Gabriel Sobrinho

unread,
Dec 2, 2013, 8:27:32 AM12/2/13
to rubyonra...@googlegroups.com
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?

Cheers,

Gabriel Sobrinho

--
You received this message because you are subscribed to a topic in the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/rubyonrails-core/rwzM8MKJbKU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to rubyonrails-co...@googlegroups.com.

Rodrigo Rosenfeld Rosas

unread,
Dec 2, 2013, 8:40:15 AM12/2/13
to rubyonra...@googlegroups.com
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... :(

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

Rodrigo.


Em 02-12-2013 11:27, Gabriel Sobrinho escreveu:
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.
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.

Egor Homakov

unread,
Dec 2, 2013, 9:15:07 AM12/2/13
to rubyonra...@googlegroups.com
> 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.

Egor Homakov

unread,
Dec 2, 2013, 9:19:16 AM12/2/13
to rubyonra...@googlegroups.com
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.

Gabriel Sobrinho

unread,
Dec 2, 2013, 9:52:53 AM12/2/13
to rubyonra...@googlegroups.com
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?

Cheers,

Gabriel Sobrinho

Greg Molnar

unread,
Dec 2, 2013, 9:57:54 AM12/2/13
to rubyonra...@googlegroups.com
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. 

Rodrigo Rosenfeld Rosas

unread,
Dec 2, 2013, 10:24:30 AM12/2/13
to rubyonra...@googlegroups.com
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?

Best,
Rodrigo.

Egor Homakov

unread,
Dec 2, 2013, 10:30:52 AM12/2/13
to rubyonra...@googlegroups.com
-1 for while(true). Let's consider all use cases

1) JS templates are used with AJAX, thus the X-Requested_with header is sent and .xhr? is enough protection
2) JS templates are also used with inline <script> tags. Thus while(true) will both break on origin website and on attacker's website. Not an option
3) Since we can send additional header why bother with prepending while(true);
So .xhr? is most elegant way that covers most of attack vectors. 
I really don't understand why JSON-hijacking wasn't solved the same way. while(true) is uglyish

Rodrigo Rosenfeld Rosas

unread,
Dec 2, 2013, 10:35:16 AM12/2/13
to rubyonra...@googlegroups.com
Yes, it makes sense, because you're forced to use XHR anyway when you include a "while(1);" otherwise you won't be able to strip it out.

Maybe the reason was to protect against old browsers that didn't implement proper same-origin policy?
--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.

Egor Homakov

unread,
Dec 2, 2013, 10:41:58 AM12/2/13
to rubyonra...@googlegroups.com
What do you mean? Hijacking issue was just same, browsers were same, and I recall XHR could add additional headers.. so while(1) sounds like weird idea 

Greg Molnar

unread,
Dec 2, 2013, 10:45:39 AM12/2/13
to rubyonra...@googlegroups.com
So you say Egor if rails would raise an error when the request for a js format is not xhr? it would solve the security issue?

DHH

unread,
Dec 2, 2013, 10:47:07 AM12/2/13
to rubyonra...@googlegroups.com
Please stop conflating the discovery of a security issue with the philosophical waxing about architecture. It's not helping the case. As stated previously, responding with JS is not only a wonderful architectural pattern, it's also not going anywhere. Not in a gem, not in a deprecation, not anywhere. We'll fix the security issue, and Rails will continue to proudly champion the use of this great pattern.

Guess what, it won't be the last security issue Rails ever has. Just like it won't be the last security issue any piece of software ever has. But we need to level up as a community in our handling of these issues.

Frankly, I'm surprised that people are willing to hire Homakov for any work in the area given his reputation for irresponsible disclosure. Finding a legit security issues is a great services, but disregarding all security issue management protocols in their publication is doing a disservice to all who would otherwise benefit from the work.

Rails has had a codified security process for many years now. It's available for all to read on http://rubyonrails.org/security. Making a blog post on your personal site isn't one of the channels listed as a responsible way of disclosing discoveries. Posting specific 0-day attack vectors against affected sites is not one either.

Making a public report over a holiday weekend, and then, when the response to the report doesn't immediately follow the proposed solution (remove the feature), go off the reservation with specific attacks is just plain irresponsible. No two ways about it. It also goes to undermine any other recommendations or suggestions coming from said reporter.

So. Damage is already done for this issue. But lest it encourages others to act as irresponsibly as Homakov has done of this issue, I hope others take a broader approach for future issues. Report systemic or framework issues per the reporting instructions on http://rubyonrails.org/security. Report specific application issues directly to application developers responsibly per their reporting instructions (see https://37signals.com/security-response for the one we use at 37signals).

Presumably we're all in the same boat here: Make Rails better and more secure. Let's row like we mean that. The Rails security team (Michael Koziarski, Jeremy Kemper, and Aaron Patterson) has worked hard in the past to provide us with a good process, they've followed that process, and they deserve our thanks and support.

Rodrigo Rosenfeld Rosas

unread,
Dec 2, 2013, 10:52:04 AM12/2/13
to rubyonra...@googlegroups.com
David, first I must say I admire both you and Homakov and I'd certainly
hire him if I could afford it.

I believe what led him to create that post was exactly your reponse to
his e-mail.

I agree he shouldn't have created this discussion publicly but you
shouldn't have replied the way you did either. You should instead try to
understand the problem first before saying it would go nowhere.

I believe this is what caused Homakov reaction.

Sincerely,
Rodrigo.
> --
> You received this message because you are subscribed to the Google
> Groups "Ruby on Rails: Core" group.
> To unsubscribe from this group and stop receiving emails from it, send

Greg Molnar

unread,
Dec 2, 2013, 11:01:07 AM12/2/13
to rubyonra...@googlegroups.com
Rodrigo, many great things around the word can be dangerous if used in the wrong way but there are great people who try to make the usage of tools secure so humanity can profit from them. 
I am not the fun of don't use this or that because it can be dangerous. Let's find a way to make it secure. That's harder but better in the long run I think.

David Heinemeier Hansson

unread,
Dec 2, 2013, 11:01:21 AM12/2/13
to rubyonra...@googlegroups.com
What email is that? I replied to Homakov on Twitter thanking him for the discovery and stating a clear intent to get the issue resolved. What I rejected outright was the knee-jerk reaction to remove the possibility of generating JS responses from Rails. This rejection was confirmed when it got clear that the motivation behind that specific mitigation strategy was also motivated by architectural opinions on what's dinosaur and what's not.

But again, even having this discussion here or on Twitter simply isn't the proper forum to discuss 0-day exploits. It's the reason we have a standardized security reporting and response protocol. It's why we go to great lengths to coordinate proper fixes across multiple versions of Rails, following the CVE process, and other responsible steps in the process.

To sidestep all that doesn't help anyone but Homakov in the short-term to build a reputation as a take-no-prisoners grey hatter. I question the business strategy of that long-term (imagine having a business dispute with Homakov after giving him access to your system -- yikes!).

Again, my opinion of the process is removed from the value of finding security holes. Of course finding and responsibly disclosing security holes is a good thing. I just wish that Homakov, and others who might be inspired by his tactics, would realize that there's plenty of gain to be had personally by subscribing to these time-tested practices.
> You received this message because you are subscribed to a topic in the Google Groups "Ruby on Rails: Core" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/rubyonrails-core/rwzM8MKJbKU/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to rubyonrails-co...@googlegroups.com.

Javan Makhmali

unread,
Dec 2, 2013, 11:09:11 AM12/2/13
to rubyonra...@googlegroups.com
As many have already mentioned, enforcing request.xhr? for js requests provides adequate protection. You can't set custom headers when making a cross-domain request with a <script> tag so checking for one ensures the request is local. That's what request.xhr? does.

Here's the solution we're using: https://gist.github.com/javan/7725255

I could see wrapping that up into something convenient like protect_from_forgery and making it a default in Rails.

-Javan
37signals

Rodrigo Rosenfeld Rosas

unread,
Dec 2, 2013, 11:13:20 AM12/2/13
to rubyonra...@googlegroups.com
Your first message in this thread. He even quoted the relevant part in
his article:

"Not only are js.erb templates not deprecated, they are a great way to
develop an application with Ajax responses where you can reuse your
server-side templates. It's fine to explore ways to make this more
secure by default, but the fundamental development style of returning JS
as a response is both architecturally sound and an integral part of
Rails. So that's not going anywhere.

So let's instead explore ways of having better escaping tools, warnings,
or defaults that'll work with the wonders of RJS."

I understand you had the best of the intentions, but I wouldn't have
started the discussion by ending it. By this time you were not yet aware
on how applications were affected and if there was a sane way to fix
this issue. If I were you, I'd first *ask* if there's an alternative way
instead of deprecating its usage *before* deciding it wouldn't go anywhere.

I guess that was what pissed him off...

I agree with you that this is not the "correct" way of dealing with the
situation, from an ethics perspective, but let's be honest: Homakov
wouldn't probably be a known name if he hadn't put the attack to GitHub
into practice. I'm not defending this approach to self marketing, but
you can't tell us it isn't effective ;)

Best,
Rodrigo.

Gabriel Sobrinho

unread,
Dec 2, 2013, 11:17:32 AM12/2/13
to rubyonra...@googlegroups.com
This fix seems great.

Homakov, what do you think? May fix the problem?

Cheers,

Gabriel Sobrinho

Egor Homakov

unread,
Dec 2, 2013, 11:18:23 AM12/2/13
to rubyonra...@googlegroups.com
There's a precise reason why I did so called "full disclosure"

I started writing an email to security@37singals, then i checked out the previous conversation we had, where i was asked about my link for Hall of Fame. I gave the link and after a month it wasn't added. No, i don't really care, but given some other details (it was the second RJS bug in basecamp, "RJS bug" overall wasn't 0 day at all, your ultimate rejection of RJS removal) i somehow ended up writing a post with full disclosure. 

I *mostly* follow responsible disclosure policies. But this is not my duty. Especially when i have couple of reasons to follow other policy.

David Heinemeier Hansson

unread,
Dec 2, 2013, 11:18:59 AM12/2/13
to rubyonra...@googlegroups.com
I feel entirely comfortable with shooting down REMOVE THE FEATURE as the first response to any security reporting. Our response should be, as it was here -- as it always is, let's fix the issue. This is doubly so when security reportings are conflated with other architectural agendas, like "dinosaur feature removal quests".

As it turns out, there is a very reasonable proposal for fixing this as well: Check js requests for xhr? and add the xhr header to all js requests through the framework, so it'll work for GET as well. We'll get this proposal vetted, of course, but if that pans out, it would be an entirely invisible fix that requires zero adjustment of the architectural style. Just like I mentioned it would, and as Homakov rejected outright initially as a possibility.

Yes, there are many ways to make a buck, if you don't care about how you do it. I don't find that worthy of celebration.

David Heinemeier Hansson

unread,
Dec 2, 2013, 11:27:55 AM12/2/13
to rubyonra...@googlegroups.com
I'm sorry that you weren't added to the hall of fame in a timely manner, and I'll check into what happened, but that hardly justifies this shotgun approach to disclosure. Had you also contacted the other application makers for which you provided 0-day attacks? Did they also slight you in some way that justified this treatment?

I'm just trying to figure out what the thought process is here, so we can calibrate our response for next time. If you get a response that a certain architectural style isn't going to be changed as you first suggest, should the community expect that you'll blast random applications with specific attack vectors on a 0-day basis?

And if the reasons you describe are enough to, in your mind, warrant the 0-day option, should potential clients expect that similar minor disputes or grievances might result in 0-day public releases from the code bases you have examined?

You're obviously skilled at finding security issues. That's a great skill to have, and it's valuable to the community when wielded with care. But you're not wielding it with care.

Egor Homakov

unread,
Dec 2, 2013, 11:30:44 AM12/2/13
to rubyonra...@googlegroups.com
> Homakov rejected outright initially as a possibility
Sorry for using "REMOVE THE FEATURE" wordings, now I see how it sounded. All i wanted is to fix security bug itself, throwing the feature away wasn't my goal. 

David Heinemeier Hansson

unread,
Dec 2, 2013, 11:37:04 AM12/2/13
to rubyonra...@googlegroups.com
Homakov, what we all want is to fix security bugs. There isn't any justification for thinking that we aren't all in the same boat on that. Things get muddy when you hinge the reporting of a security issue to your personal assessment of what a good architectural pattern is, though.

So please. I implore you to change your ways. You can be a great part of this community and you can build a reputable security consulting business without resorting to these irresponsible tactics.

We will get this specific issue fixed shortly (as soon as the GET .js solution by adding xhr headers has been vetted), I'm not in the least concerned about that. I'm concerned that we'll be right back at square one next time you find an issue and you arm the internets with 0-day exploits against a random selection of applications before giving the makers a reasonable timeframe to defend themselves (one holiday weekend isn't it).

Zack Siri

unread,
Dec 2, 2013, 1:03:30 PM12/2/13
to rubyonra...@googlegroups.com
I think i may have a solution, if i am wrong please don't shoot me ;)

Zack Siri

unread,
Dec 2, 2013, 1:06:39 PM12/2/13
to rubyonra...@googlegroups.com
I may have a possible solution, check it out (sorry if i double posted not sure if my previous post went through, as I have shitty internet)

Kontax on Heroku


Xavier Noria

unread,
Dec 2, 2013, 1:12:14 PM12/2/13
to rubyonrails-core
In this thread it has been repeated several times that .js endpoints via GET are a security breach. And that people should stick to JSON.

Let me make clear for the archives that is not generally the case. There are valid use cases for dynamically generated public JavaScript, for example when your application exposes a widget 3rd party clients request to have their DOM modified with content. Think Disqus. I have implemented centralized rating systems for hotel providers that work that way.

The potential problem happens when your JavaScript GET endpoint exposes sensitive/private data.

Now, since the former is a rare use-case compared to the latter, the XHR protection should probably be enabled by default, but you need still to be able to opt-out.


David Heinemeier Hansson

unread,
Dec 2, 2013, 1:14:30 PM12/2/13
to rubyonra...@googlegroups.com
Xavier, absolutely. Javan has suggested we have something like protect_from_forgery, which can be opt-out of on a per action/controller basis.

Marius Corîci

unread,
Dec 2, 2013, 1:29:46 PM12/2/13
to rubyonra...@googlegroups.com
I much appreciate RoR, its community Homakov included. I never saw RoR security team at least one time to appreciate Homakov's findings. Perhaps you should take it closer by first recognize his security work over RoR. I'm quite sure that Homakov would have comply to your rules in the end.

Egor Homakov

unread,
Dec 2, 2013, 1:43:07 PM12/2/13
to rubyonra...@googlegroups.com
I am trying to imagine "dynamically generated public JavaScript" but nothing comes to my mind. Any small bit of information, even the fact that user is logged in, or blocked, is somewhat sensitive IMO.
Opt-out is must-have indeed. E.g. in development env it might be convenient to reach .json endpoints directly.

Xavier Noria

unread,
Dec 2, 2013, 2:11:24 PM12/2/13
to rubyonrails-core
On Mon, Dec 2, 2013 at 7:43 PM, Egor Homakov <hom...@gmail.com> wrote:

I am trying to imagine "dynamically generated public JavaScript" but nothing comes to my mind.

This is an old trick.

Your service provides a small JavaScript snippet for hosting sites to embed. The snippet generates a SCRIPT tag in the hosting DOM whose creation triggers a (GET) request to fetch JavaScript from the central service, in the provider's domain. That as you know is not subjected to the same-origin policy, hence the technique.

See for example the snippet of Disqus:


The response contains JavaScript, whose evaluation injects content in the host page.

The user browsing the hosting website is not a user of the service provider, users may not even realize there is a centralized service providing that section of the page.


Javan Makhmali

unread,
Dec 2, 2013, 2:16:06 PM12/2/13
to rubyonra...@googlegroups.com
Embeddable gists are another example: <script src="https://gist.github.com/javan/7725255.js"></script>

Pier-Olivier Thibault

unread,
Dec 2, 2013, 7:00:30 PM12/2/13
to rubyonra...@googlegroups.com
I've been using RJS for quite a while and I think it's a wonderful way to render server-side template and form errors. I agree with everyone that this issue is important as my ticket shows (https://github.com/rails/rails/issues/11509).

I've had a somewhat crazy idea a few months back and I'm sorry if I side track the issue at hand. I am currently thinking about using a websocket for this kind of query (I have a very rough alpha working). The gist would be to have a websocket open on DOMContentReady (there wouldn't be any reconnection due to the nature of turbolinks). From there, every ajax request would be made through the websocket instead of the normal HTTP procedure. This, as far as I understand, would have the advantage of making XSS impossible (Websockets follows the same-origin policy).

Right now, this idea would be somewhat of a hack for different reason: A Javascript client script would have to take every click event on a[data-remote=true] and take the URL and pass it to the websocket which would then use Journey as normal to dispatch the request to the right controller. The response would then be sent through the websocket (The headers could be entirely drop).

I think this idea could possibly fix this issue altogether while being completely transparent to developers. 

Do I make sense? Is it a stupid idea?

Maurizio De Santis

unread,
Dec 2, 2013, 7:30:40 PM12/2/13
to rubyonra...@googlegroups.com
Marius, your solution is not much different from responding a render :json : you send only the data, and the processing code is already on client side; transponder.js seems to me that it adds an abstraction level in order to manage the data, but it is different from RJS, which sends the execution code too, that is necessary for running the examples made before, like Disqus, or single page apps.

transponder.js a good solution if you don't want / need to send the execution code, as render :json , but I think I got that RJS is necessary for other contexts.

--

Maurizio De Santis


2013/12/2 Marius Corîci <marius...@gmail.com>

--

Maurizio De Santis

unread,
Dec 2, 2013, 7:31:14 PM12/2/13
to rubyonra...@googlegroups.com
Sorry, I meant Zack.

--

Maurizio De Santis


2013/12/3 Maurizio De Santis <desantis...@gmail.com>

Egor Homakov

unread,
Dec 8, 2013, 11:19:24 PM12/8/13
to rubyonra...@googlegroups.com
so if/when this will make it to master?

David Heinemeier Hansson

unread,
Dec 9, 2013, 12:51:41 AM12/9/13
to rubyonra...@googlegroups.com
Jeremy Kemper is assigned to this. We will get this in shortly. 
Message has been deleted

Pier-Olivier Thibault

unread,
Dec 13, 2013, 7:11:03 PM12/13/13
to rubyonra...@googlegroups.com
Just had a flash about this issue.

JSON has a similar issue and it solved it by prepending 'for (;;);' before the JSON payload.

Wouldn't it be an idea to prepend any js.erb template with 'for (;;);' and use String.substr(9) (9 being the size of the for loop) to remove that loop before injecting the payload in the <script>. This way, JS would be safe without need to do extra verification on the server side.

Egor Homakov

unread,
Dec 14, 2013, 7:44:06 AM12/14/13
to rubyonra...@googlegroups.com
that solution is ugly. for(;;) means code on server side to add it and code on client side to slice. While .xhr? check is one line in ApplicationController.

Jeremy Kemper

unread,
Dec 17, 2013, 4:55:42 AM12/17/13
to rubyonra...@googlegroups.com
For Rails 4.1: https://github.com/rails/rails/pull/13345

Since we don't know the response format until rendering, it's simplest
to use an after_action to verify that we aren't serving JS to a
non-XHR GET request.

This piggybacks on the same `protect_from_forgery` declarations that
apps already use, so they'll transparently get protection without
changing anything.

Apps that intentionally expose JavaScript responses (like third-party
widgets, per-customer API embeds, etc) will need to exclude those
actions using existing `protect_from_forgery` API.

Thanks everyone for the (long) discussion and thanks to Egor for the
initial report - months ago now! - and this reminder.
> You received this message because you are subscribed to the Google Groups
> "Ruby on Rails: Core" group.
> To unsubscribe from this group and stop receiving emails from it, send an
Reply all
Reply to author
Forward
0 new messages