SJR and XSS can be as secured without too much effort.

SJR (Server generated javascript) made the headlines Iast week in the ruby on rails community. The main concern (which is a valid one) is that it can leak private informations by using specially crafted website. Some people are already working on a fix. Still, I think you might want to have a fix on your project right now so I thought I would share it with everyone interested. First, a little bit of history.

JSON Payload has a similar issue

The solution many people recommended was to switch the architecture to a JSON based format and use client side JavaScript to handle the data and present it to the user. The problem is that JSON suffers from a possible vulnerability very similar to SJR's vulnerability. It's called JSON Hijacking. Before you start having a heart attack, you need to understand that browsers have fixed this issue a while ago so that anyone with a recent browser is protected against the hijack.

However, old browsers are still vulnerables.

Google, Facebook and many other have fixed this vulnerability by prepending their payload with an infinite loop. Because you can alter the content before evaluating it in an AJAX environment, you can remove the infinite loop beforehand. In the hijacking situation, it's impossible for the attacker to modify the content of the payload which would launch the infinite loop everytime.

Here's how Google does it.

while(1);
{
  my: "data",
  is:"now secure",
}

while(1); can also be replaced by for(;;); which is the same thing while it has 1 less character —  saving bandwidth.

The goal here is that when requesting the data via AJAX (which follows the same origin policy), you can alter the data to strip the infinite loop before processing it.

If, on the other hand, you want to insert the URL in a script tag, the content of the data would be process as-is, implementing an infinite loop and crashing the browser.

Using the same technique with SJR

So I set myself a goal to implement a solution that wouldn't break my workflow and would require the less amount of work to get working. What I wanted to do was to configure Rails to prepend an infinite loop on any request that would return Javascript code. On the client side, I would look at the payload and strip the infinite loop if the data starts with one, then execute the remaining piece of code.

Layouts

First step to patch this issue is to add the infinite loop to all JS payload. Everyone must be familiar with the layout file application.html.erb. The same kind of layout can be used to patch this issue as well.

Create a new layout called application.js.erb. This guy will be used every time a JS request will be sent by the server.

# app/views/layouts/application.js.erb
for(;;);(function() {
  <%= yield %>
})()

Line #1 introduce the infinite loop. Then I added a closure, I really like to wrap all my JS payloads in closure. Now all your JS code can be kept becomes protected by this technique with a minimal footprint.

But the the solution isn't working yet. If you had tried it out, your browser would have hung forever: the infinite loop in action!

Remove the infinite loop on the client side

Here, I'll use jQuery as the basis of my example. I know there's many great JS frameworks out there so your mileage may vary. Keep in mind that the solution I give here should be very similar to what you would have to do on your setup.

The goal is to check for the first 8 characters (for(;;); is equal to 8 characters) and if it is, slice the string payload to remove those character before evaluating it.

// app/assets/javascript/application.js.erb
$(function() {
  var globalFunc = $.globalEval;
  $.globalEval = function(data) {
    if (data.slice(0, 8).localeCompare("for(;;);") == 0) {
      data = data.slice(8)
    }
    globalFunc(data);
  }
})

Secure SJR in action

Now, when you will be dealing with your AJAX request, everything should work as expected. However, if someone with malicious intent would try to use your endpoint inside a <script> tag, it would launch an infinite loop that would render the payload useless.