Crowley Code! 
 (Take 12)

JSONRequest amendment 2007/09/23

JSONRequest was born from Crockford's head more than a year ago, but only this month has it really gotten interesting. Collin Jackson has released a Firefox extension implementing the JSONRequest spec. I was naturally compelled to play with it.

JSONRequest is a proposed new standard that could replace XMLHttpRequest and shed the crippling "same domain policy" that makes it a pain to consume outside APIs in Javascript. It's secure enough for cross-site-scripting for a couple of reasons. I'm lazy, so here are Crockford's words:

  1. JSONRequest does not send or receive cookies or passwords in HTTP headers. This avoids false authorization situations. Knowing the name of a site does not grant the ability to use its browser credentials.
  2. JSONRequest works only with JSON text. The JSONRequest cannot be used to access legacy data or documents or scripts. This avoids attacks on internal websites which assume that access is sufficient authorization. A request will fail if the response is not perfectly UTF-8 encoded. Suboptimal aliases and surrogates will fail. A request will fail if the response is not strictly in JSON format. A request will fail if the server does not respond to POST with a JSON payload.
  3. Reponses will be rejected unless they contain a JSONRequest content type. This makes it impossible to use JSONRequest to obtain data from insecure legacy servers.
  4. JSONRequest reveals very little error information. In some cases, the goal of a miscreant is to access the information that can be obtained from an error message. JSONRequest does not return this information to the requesting script. It may provide the information to the user through a log or other mechanism, but not in a form that the script can ordinarily access.
  5. JSONRequest accumulates random delays before acting on new requests when previous requests have failed. This is to frustrate timing analysis attacks and denial of service attacks.

From this list, #1 and #3 make the most difference. Without sending cookies, it will be very difficult to send requests impersonating a logged-in user. By requiring the application/jsonrequest content type on the response, the standard requires that the API provider take action to allow JSONRequests. This leaves no excuse for lazy API providers who don't make their API secure.

As for other security holes, I've been poking around a bit against the Flickr API. I had to steal an API key, the shared secret for that key and a user's token that came from that API key before I could do anything nasty. Now, since getting these three pieces of information gives you free reign even without JSONRequest, I don't see much of a threat here.

As a bit of an aside, OAuth seems to work very much like the Flickr API and so JSONRequest will be equally innocuous for OAuth users.

OK, finally the amendment part

Requiring that all POST requests be sent as well-formed JSON data severely limits JSONRequest's ability to take web apps to the next level. The inability to send traditional POST payloads isn't such a big deal, but the inability to send multipart POSTs for file uploads is tragic.

Of course, it isn't as easy as just lifting the JSON-only restriction on POST payloads. As Mike pointed out to me last night, old sites using GET variables to handle authentication (PHPSESSID anyone?) are pretty much sitting ducks. But there's always another way.

Allowing traditional or multipart POSTs without some prior setup can be hazardous. Although these will always return an error to the caller, on the server side they may be successful which could really muck up your databases. The solution is simply to require a successful JSON POST immediately prior to accepting the multipart request as a way for the server to say "yes, I want that multipart POST." In pseudocode, something like this:

JSONRequest.post_multipart = function(url, send, done, timeout) {
  JSONRequest.post(url, {'multipart': 1}, function(requestNumber, value, exception) {
    if (value) {
      // Here we would take params passed to post_multipart and actually
      // send the multipart request.  The result of that would be passed
      // to the function the called passed in the done param.  The
      // intermediate JSON POST is essentially invisible.
    } else {
      console.log(exception.message);
    }
  }
};

With this we will finally have hack-free file uploads in addition to safe cross site scripting. Even Microsoft can't say no to implementing that.

Comments (0)

Richard Crowley?  Kentuckian engineer who cooks and eats in between bicycling and beering.

I blog mostly about programming and databases.  Browse by month or tag.

To blame for...


© 2009 Richard Crowley.  Managed by Bashpress.