Wednesday, March 29, 2006

JSONRequest, Part 2 (Cross Domain Policy for All)

As mentioned in part 1, instead of introducing a new request object, JSONRequest, I would prefer to fix the current available pathways. However, I can appreciate that it may be hard to do that. If JSONRequest does go forward, here are some suggestions. The goal of these suggestions are to get JSONRequest to behave in a safe, useful way that could be ported to the other pathways, like script src or XMLHTTPRequest, if it all seemed to work out (and maybe one day merge JSONRequest with XMLHTTPRequest into a generic request object).

The main goal of these suggestions are to protect legacy server systems that did not expect cross domain requests. These suggestions require a server to do explicit actions to allow cross domain behavior. This should address most of the security concerns while laying the groundwork for a general cross-domain request model for the browser.

These suggestions are only for cross-domain requests made with JSONRequest. XMLHTTPRequest's same origin behavior can be used for JSONRequests to the same origin server.

I do not have any particular attachment to the names of the attributes and headers used below. They are just used for illustration of the concept.

The suggestions:
  • Allow Cookies with a xdallow Attribute
  • Request Origin Header
  • Cross-Domain Policy for Pages
  • Allow GET requests to Allow Caching
Allow Cookies with a xdallow Attribute

To alleviate the concerns about data stealing and impacts on legacy systems, introduce a new attribute for cookies, xdallow. New server services could use this new attribute to indicate that it wants to allow cookies to be sent in JSONRequests that originate from a page that is outside of the server's domain. Like today's cookies, the cross domain page cannot actually see the value of these cookies, but if it makes a JSONRequest to a server that set these cookies, they would be sent (as long as the request conformed to the policy set by this new cookie attribute):

Using an example from the cookie spec, current cookies can look like this:
Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; domain=anvil.acme.com; expires=Wednesday, 09-Nov-99 23:12:40 GMT
The new attribute would look like this:
Set-Cookie: CUSTOMER=WILE_E_COYOTE; xdallow=XDALLOWVALUE; path=/; domain=anvil.acme.com; expires=Wednesday, 09-Nov-99 23:12:40 GMT
where XDALLOWVALUE could be:
  • xdallow=all - all pages are allowed to use a request to the origin server with this cookie.
  • xdallow=domain.one.com,domain.two.com - a list of comma-separated domains
  • xdallow=http://a.domain.com/with/path.html - a full URL to allow. If the server wanted to allow multiple domains, send multiple Set-Cookie headers with different xdallow values.
  • xdallow=/path/to/policy.xml - A path to the server's cross-domain cookie policy. It would be in XML format and could allow specifying a whitelist and black list of domains and paths that are allowed.
Request Origin Header

The spec uses a Domain header to specify the server that is making the request, and the value is derived from document.domain. I would prefer not to rely on document.domain, since it is a script property that can be changed to a limited degree.

I would prefer to use the page's real domain and include the path of the page making the request. Effectively like the Referer header. I would like to just use the Referer header, but I can see for cross-domain requests only, a new header name should be used. In particular, if these changes were ever merged with the XMLHTTPRequest capability, a header that cannot be modified by a setHeader command should be used. So, perhaps use a new header name like:
Request-Origin: http://some.domain.com/path/to/page/making/request.html
This header would even be sent for https requests. If this is problematic, then don't allow https pages to make cross domain requests.

Cross-Domain Policy for Pages

This is a mechanism for checking if a web page wants to allow cross domain requests via JSONRequest. This is to mitigate the case when the web page is compromised by malicious script. If JSONRequest is asked to make a cross-domain request, it first checks with the server that the page originates from for a xdallow policy file. This policy file could be similar to the one mentioned in the cross-domain cookies section.

If the example page is at the following URL:

http://some.domain.com/path/to/web/page.html

JSONRequest would check the following paths in this order:
  1. http://some.domain.com/path/to/web/.xdallow/page.html.xml
  2. http://some.domain.com/path/to/web/.xdallow/
  3. http://some.domain.com/.xdallow/
Allow GET requests to Allow Caching

The spec does not allow for caching of responses or GET requests. If the cookies are not allowed for JSONRequest, I believe the majority use case for JSONRequest will be for non-authenticated APIs, and a majority of those cases could benefit from cached responses. I don't believe POST requests can be effectively cached by browsers or proxy caches, so it is important in that respect to allow GET requests. Respecting cache headers would also be required of JSONRequest.

If all of the above recommendations are used, it helps protect GET requests. Legacy services would still be protected since they don't support JSON responses. As servers roll out support for JSONRequest, they will be aware of the above requirements to allow cross-domain usage.

In general, these additions force newer services to do explicit actions to allow the cross-domain requests and the additions give those services the information to make intelligent, secure decisions. Legacy systems are protected if they don't opt-in to these requirements. They would have very strong protection by just disallowing any request with the Request-Origin header.

Tuesday, March 14, 2006

JSONRequest Thoughts

This is feedback related to JSONRequest.

Specific detail comments

I'm curious why it is designed as a static method invocation and request ID vs. XMLHTTPRequest's instance-based approach. I would prefer to match XHR's instance-based approach, unless there is a good reason to avoid it. It would be good to match the XHR idiom as much as possible, since developers are familiar with it.

I also like the idea of request limiting on failure count. It would probably be good even for scenarios when it is not an attack, but the server is just having problems. No need to pound the server relentlessly.

General Problem Comments

In general, it feels too limiting, and I'm not sure the limits do much in the end to make it a viable alternative to just dynamically adding SCRIPT tags to the HTML head to load cross-domain scripts and/or JS data. With the dynamically added SCRIPTS, you get cookies sent to the domain, and have the option to get more than just a JSON object.

It is dangerous, but the point is it is possible to do it today. The only thing missing is being able to do a POST and getting a standard callback to know the script has loaded. I tried solving those things with the Dynamic Script Request (DSR) API.

Even though it can be dangerous, developers can use it today, and if it solves their problem, then they will use it. If it is possible to get browser makers to do some work in the area of cross domain script usage, I would prefer to see that effort used to strengthen the existing method and to provide the ability to specify a page-level script policy concerning cross domain scripts.

Strengthen Existing Pathway

I would like to see the following things added to to script tags, and/or have a ScriptRequest object to encapsulate the behavior (I don't really care about the names of these attributes, just what capabilities they allow):
  • method="POST"
  • data="url encoded data sent as the POST body"
  • sync="true" (to get blocking requests)
  • onload="event callback that really worked and was called when the script was loaded"
  • createScope="true" (or something that says "don't treat read the data and do the equivalent of a window.eval () on it. Instead, treat the data as specific scope or as instance object).
Maybe instead of onload, onreadystate, to allow Comet-style connections (the "Duplex" section in the JSONRequest document)?

The HTTP request headers could not be modified by the script request object (except indirectly, in the case of the cookie header). In addition, I would require that a Referer HTTP header be sent with the request. There was a comment on the WHATWG mailing list that Referer is not sent for HTTPS requests. I think something needs to be sent to indicate the calling domain and page path in the HTTP headers so that the server handling the request can make a decision on whether to allow the request.

Page Level Cross Domain Script Policy

Cross domain XML policy files on the server (like the ones used by Flash) do not seem sufficient. As far as I know, those only allow setting domain-to-domain trusts, but there could be many types of web applications on a domain (one HTML page can be a whole application). Sites can host pages from many users.

It should be possible to specify a cross domain script policy in a web page. Something that would set the permissions for scripts that come from other domains than the current domain. It could even be used for any exterior scripts that are loaded, even ones on the same domain. I'm not too particular on that, but it might be a good idea.

The policy would be specified by an HTML element, a child of HEAD. I do not care what the tag is called, but for the purposes of illustration, I'll use a META tag. But again, I do not have any special feelings about using META:

<meta name="ExternalScriptPolicy" content="dom=yes,cookie=no,request=no">

Different script capabilities can be allowed/disallowed, like whether to allow:
  • DOM access
  • Cookie access (maybe break it out by read and write)
  • Request access (whether the external script can use XMLHTTPRequest or ScriptRequest)
  • Other restrictions/privileges?
This tag could not be modified by script.

Conclusion

JSONRequest has some nice ideas, but since there is a gaping hole with SCRIPT tags anyway, I would prefer to use the browser makers time to improve the SCRIPT pathway, make it better to identify request origins, and provide page-level script security policies.