Book Excerpt: The Tangled Web: A Guide to Securing Modern Web Applications
By Michal Zalewski
January 17, 2012 —
(Page 4 of 6)
Related Search Term(s): The Tangled Web: A Guide to Securing Modern Web Applications
Non-simple Requests and Preflight
In the early drafts of the CORS protocol, almost all requests were meant to be submitted without first checking to see if the server was actually willing to accept them. Unfortunately, this design undermined an interesting property leveraged by some web applications to prevent cross-site request forgery: Prior to CORS, attackers could not inject arbitrary HTTP headers into cross-domain requests, so the presence of a custom header often served as a proof that the request came from the same origin as the destination and was issued through XMLHttpRequest.
Later CORS revisions corrected this problem by requiring a more complicated two-step handshake for requests that did not meet the strict “simple request” criteria outlined in “CORS Request Types” on page 236. The handshake for non-simple requests aims to confirm that the destination server is CORS compliant and that it wants to receive nonstandard traffic from that particular caller. The handshake is implemented by sending a vanilla OPTIONS request (“preflight”) to the target URL containing an outline of the parameters of the underlying XMLHttpRequest call. The most important information is conveyed to the server in three self-explanatory headers: Origin, Access-Control-Request-Method, and Access-Control-Request-Headers.
This handshake is considered successful only if these parameters are properly acknowledged in the response through the use of Access-Control-Allow-Origin, Access-Control-Allow-Method, and Access-Control-Allow-Headers. Following a correct handshake, the actual request is made. For performance reasons, the result of the preflight check for a particular URL may be cached by the client for a set period of time.
Current Status of CORS
As of this writing, CORS is available only in Firefox and WebKit-based browsers and is notably absent in Opera or Internet Explorer. The most important factor hindering its adoption may be simply that the API is not as critical as postMessage(...), its client-side counterpart, because it can be often replaced by a content-fetching proxy on the server side. But the scheme is also facing three principal, if weak, criticisms, some of which come directly from one of the vendors. Obviously, these criticisms don’t help matters.
The first complaint, voiced chiefly by Microsoft developers and echoed by some academics, is that the scheme needlessly abuses ambient authority. They argue that there are very few cases where data shared across domains would need to be tailored based on the credentials available for the destination site. The critics believe that the risks of accidentally leaking sensitive information far outweigh any benefits and that a scheme permitting only nonauthenticated requests to be made would be preferable. In their view, any sites that need a form of authentication should instead rely on explicitly exchanged authentication tokens.
The other, more pragmatic criticism of CORS is that the scheme is needlessly complicated: It extends an already problematic and error-prone API without clearly explaining the benefits of some of the tweaks. In particular, it is not clear if the added complexity of preflight requests is worth the peripheral benefit of being able to issue cross-domain requests with unorthodox methods or random headers.
The last of the weak complaints hinges on the fact that CORS is susceptible to header injection. Unlike some other recently proposed browser features, such as WebSockets (Chapter 17), CORS does not require the server to echo back an unpredictable challenge string to complete the handshake. Particularly in conjunction with preflight caching, this may worsen the impact of certain header-splitting vulnerabilities in the server-side code.