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.

No comments: