Login Register

A Note on "JavaScript Hijacking"

So the misleading and badly researched articles have started to filter in, and were you to believe them (instead of actually reading Fortify's paper), you might start to wonder about the wisdom of using JavaScript.

The bad news is that for badly written server-side components that serve up sensitive data via JSON at well-known URLs, there is a problem. The good news is that you probably don't have this problem (yet) and that despite the hyperbole, Dojo isn't "vulnerable". Should your application fit the vulnerable profile, it's also simple and straightforward to mitigate the risk. Dojo will be including patches in 0.4.3 to inform developers of the potential risks their server-side components may be exposing them to and making it even easier to do the right thing on the client side, but you don't need our help to get there. Just make sure your JSON responses are enclosed in JavaScript comment charachters ("/* your json data here */"), strip those off before eval-ing the response, and you're good to go. And you only need to do this if your sever-side is exposing sensitive data via raw JSON. Here's what that pattern looks like on shipping versions of Dojo:

dojo.io.bind({
  url: "endpoint.php",
  content: { format: "json", token: someNonce },
  type: "text/plain",
  load: function(type, data){
    var resp = eval("("+data.substring(data.indexOf("\/\*")+2, data.lastIndexOf("\*\/"))+")");
    // work with the response data here
  }
});

Traditionally, when security researchers and engineers discuss vulnerabilities in a non-military context, they are discussing some active oversight or error in design or implementation that leads a system to be significantly less secure than it might otherwise be. The final conclusion section of Fortify's paper gets it right: we've got a situation where the browser vendors have repeatedly let us down and now we're all looking for workarounds. As a result, there's a structural disposition in the web toward relying on a single point of failure: the same-domain model.

So breath easy, rest assured that Dojo isn't giving up the keys to your kingdom, and give the Fortify paper a read. It's only through level-headed understanding of risks that threats, real or imaginary, are mitigated.

I agree that it's not fair

I agree that it's not fair to call Dojo "vulnerable" just because somebody used JSON (actually a particular variant of it) in a way that created a real vulnerability.

However, the lesson learned should be that the toolkit should encourage safe design pattern and highly discourage ones that cause such security issues.
For example, the application should be designed to be independent of the protocol (JSON, XML, etc) used for data transport, and the framework should make that design easy to implement.

Actually did you notice that

Actually did you notice that the exploit in the paper ONLY works for [arrays]?

If you return an {object} from your server, it's not valid JavaScript syntax without surrounding parens, so it can't be exploited in the manner described.

I told them about this before they released the paper, and they agreed, but they decided to release the paper as-is anyway!

So I am trying to determine

So I am trying to determine how this exploit would work: you must load the badscript.js from some site that is not the originating site, and that script must then make an AJAX call back to the good server, and send the info back to the bad server (ignoring data structure for now).
To do this, you would have to include badscript.js from the bad server, yet we serve everything from our secure https server. We authenticate name/password/ip address in every transaction (using https REST). Plus, everything AJAX is via post.
So does this mean that the only way to do this would be: to add an add-in somewhere in the end-user browser to add the badscript.js to the end-user html. If that is the case, then it is hard to see how this is different from a trojan-remote-control, or a key logger kind of attack. One that you cannot manage at the server side.
Please help me understand this better...

Alex - Thanks for pointing

Alex - Thanks for pointing to this paper, it's a very useful reminder and a good introduction to cross-site scripting attacks (and this particular variant thereof) for people new to some of this stuff.

All - Rather than just putting comment tags around the JSON, or adopting the Gmail "while(1);" approach (which did make me chuckle), how 'bout we try to help out the poor mark who's under attack? Put in some code redirecting the browser to a warning page:

window.location = 'http://example.com/csswarn';
/*JSON
(your JSON data goes here)
JSON*/

... and then remove everything up through the "/*JSON" at the beginning and the final "JSON*/" at the end before parsing the JSON; that should do it. The page could say something like "The page you were just viewing was attempting to steal information from you and/or from us; we have detected this attack and redirected you here to tell you about it." followed by the details of the referer field...

Doing the redirect would, of course, be up to the developer, but we could build the support for the tag stripping into dojo.json.evalJson or probably another method in that module we'd use only with externally-loaded JSON, for instance:

evalLoadedJson: function(/*String*/ json){
    // summary:
    //    evaluates the passed string-form of a JSON object,
    //    stripping off a leading /*JSON tag (and anything before it) and
    //    a trailing JSON*/ tag if found.
    //    These tags can be helpful when defending against
    //    cross-site scripting and "JavaScript Hijacking"
    //    attacks; see <a href='http://dojotoolkit.org/node/619'>this</a>
    //    blog entry for more info.
    // json: 
    //    a string literal of a JSON item, for instance:
    //    '/*JSON { "foo": [ "bar", 1, { "baz": "thud" } ] } JSON*/'
    // return:
    //    the result of the evaluation

    const START_TAG = '/*JSON';
    const END_TAG   = 'JSON*/';

    var startIndex;
    var endIndex;

    startIndex = json.indexOf(START_TAG);
    if (startIndex >= 0)
    {
        endIndex = json.lastIndexOf(END_TAG);
        if (endIndex >= 0)
        {
            json = json.substring(startIndex + START_TAG.length, endIndex);
        }
    }

    return dojo.json.evalJson(json);
}

If people like it, I'll send in my CLA (I'm new here) and post a diff for json.js to a ticket for it in trac (this probably wouldn't be Ticket #2656, as that's for a more system-wide enhancement).
--
T.J. Crowder
tj at crowdersoftware dot com