Please read my blog's comment policy here.
Update: Internet Explorer 10+ supports CORS using XMLHTTPRequest. IE11 deprecates the XDomainRequest object and it is not available in IE11 Edge mode.
In Internet Explorer 8, the XDomainRequest object was introduced. This object allows AJAX applications to make safe cross-origin requests directly by ensuring that HTTP Responses can only be read by the current page if the data source indicates that the response is public; in that way, the Same Origin Policy security guarantee is protected. Responses indicate their willingness to allow cross domain access by including the Access-Control-Allow-Origin HTTP response header with value *, or the exact origin of the calling page.
When designing the new object, our top priority was to ensure that existing sites and services would not be put at risk. To that end, we imposed a number of restrictions on what sort of requests can be made with the XDomainRequest object. Most of the restrictions are designed to help prevent Cross-Site Request Forgery (CSRF) attacks against legacy services.
The restrictions and the reasoning behind them are described by the rest of this post.
1. The target URL must be accessed using the HTTP or HTTPS protocols.
This one is simple—because the object relies on a HTTP response header for access control, the object requires that the target URL be HTTP or HTTPS so that it can examine the response headers to obtain permission to make the response available to the caller.
2. The target URL must be accessed using only the HTTP methods GET and POST
In order to ensure that the new object did not increase the attack surface against existing servers and services, we elected to restrict the HTTP methods (verbs) it may call to GET and POST. HTML 4.01 forms are restricted to these same methods, which means that any service which is at risk from the XDomainRequest object would also be vulnerable to attack from a cross-origin HTML Form. Since HTML Forms have existed for well over a decade, it’s assumed that applications have been hardened against attack from the GET and POST methods.
We could not assume that requests issued using other methods would be similarly handled by servers. Beyond that concern, most other methods that developers would hope to use (e.g. WebDAV / REST methods) also require sending custom HTTP Headers, and:
3. No custom headers may be added to the request
This restriction is similar to #2; we wanted to ensure that the XDomainRequest object would not allow an attacker to issue a request that a HTML Form could not issue. This is important because the Access-Control-Allow-Origin header isn’t available until after the response is returned, so there’s no way to tell before the request is issued whether or not the server is willing to accept cross-domain HTTP requests. Without these restrictions, a “Fire and Forget” CSRF attack could take place against a legacy server, even if the server doesn’t return the Access-Control-Allow-Origin header.
All XDomainRequest-issued requests are sent with an Origin header, indicating the Origin (scheme+hostname) of the caller.
4. Only text/plain is supported for the request's Content-Type header
In the original incarnation of the XDomainRequest object, we allowed specification of the Content-Type for a POST request. It was pointed out that this violated our goal of emitting only requests that HTML Forms can issue, because HTML Forms are limited to sending data in three different content types: text/plain, application/x-www-urlencoded, and multipart/form-data. In particular, it was pointed out that some AJAX server libraries would blindly assume that if they received a request with a SOAP or JSON Content-Type, then the client must either be trusted or Same Origin (because HTML itself previously offered no way to issue cross-origin requests with that Content-Type).
Unfortunately, when we fixed this problem in a later IE8 Beta, we went a bit too far; we restricted the content type to text/plain but didn’t allow the caller to specify that the data was in application/x-www-urlencoded form. This is problematic because server-side frameworks (e.g. ASP, ASPNET, etc) will only automatically parse a request’s fields into name-value pairs if the x-www-urlencoded content type is specified.
Note: As of 2014, XDomainRequest doesn't appear to send any Content-Type header at all. It's not clear to me when this changed.
To workaround this issue, server code that currently processes HTML Forms must be rewritten to manually parse the request body into name-value pairs when receiving requests from XDomainRequest objects. This makes adding support for the XDomainRequest object more difficult than it would be otherwise.
5. No authentication or cookies will be sent with the request
In order to prevent misuse of the user’s ambient authority (e.g. cookies, HTTP credentials, client certificates, etc), the request will be stripped of cookies and credentials and will ignore any authentication challenges or Set-Cookie directives in the HTTP response. XDomainRequests will not be sent on previously-authenticated connections, because some Windows authentication protocols (e.g. NTLM/Kerberos) are per-connection-based rather than per-request-based.
Sites that wish to perform authentication of the user for cross-origin requests can use explicit methods (e.g. tokens in the POST body or URL) to pass this authentication information without risking the user’s ambient authority.
6. Requests targeted to Intranet URLs may only be made from the Intranet Zone
As the table in the documentation shows, XDomainRequest restricts Internet-Zone pages from making requests to Local Intranet-based resources. This security precaution isn’t directly enforced by HTML Forms, but Internet Explorer’s Zone Elevation security feature provides a similar protection for navigations, of which Form Submissions are simply a specialized type.
7. Requests must be targeted to the same scheme as the hosting page
This restriction means that if your AJAX page is at http://example.com, then your target URL must also begin with HTTP. Similarly, if your AJAX page is at https://example.com, then your target URL must also begin with HTTPS.
It was definitely our intent to prevent HTTPS pages from making XDomainRequests for HTTP-based resources, as that scenario presents a Mixed Content Security Threat which many developers and most users do not understand.
However, this restriction is overly broad, because it prevents HTTP pages from issuing XDomainRequests targeted to HTTPS pages. While it’s true that the HTTP page itself may have been compromised, there’s no reason that it should be forbidden from receiving public resources securely.
Worst of all, the Same Scheme restriction means that web developers testing their pages locally using the file:// scheme will find that all of the XDomainRequests are blocked because file:// doesn’t match either http:// or https://, which are the only valid target schemes (point #1). To workaround this issue, web developers must host their pages on a local web server (e.g. IIS, the Visual Studio hosting server, etc).
To workaround this limitation, you can build a postMessage-Proxy-for-XDR.
Despite the restrictions and unintended limitations, the XDomainRequest object provides powerful functionality. As servers that support the CORS specification become more common, the object will only get more useful.
Update: Internet Explorer 10 now supports CORS using XMLHTTPRequest which should be preferred to the now-deprecated XDomainRequest object.
Note: We intended to support COMET-streaming with XDomainRequest, but AJAX developers may need to workaround one small bug in the object’s support for streaming responses.
Note: In IE8, all XDomainRequests will fail with an error when the user is browsing in InPrivate Browsing mode. This bug was fixed in Internet Explorer 9.
The XDomainRequest object works in our development environment with https-https. We are making a cross domain call. When we move the code to System testing environment it breaks. Both the url are in intranet, using https-https. We are testing with IE8 and IE9. We are getting access denied exception. Any insight why its working in one environment and not in another with the same code.
@Louis: Email me a Fiddler capture and I'll have a look. Are you *positive* the Zone settings are what you think they are?
Will xdomainrequest object support situation if not only a domain is different but a port number as well. So if a page is on http://one.example.com:90 and tries to do an ajax request to http://two.example.com:234. It fails for me but if the two.example.com is on port 90 then it works alright.
EricLaw: Going from one port to another should work just fine. What's the live URL of the repro? What browser version are you using?
Hi How does post work with XDomainRequest in MVC 3 routes?
EricLaw: I don't really understand the question. It's a plain HTTP Post, but the lack of a Request Content-Type header might make it tricky to interoperate with ASP.NET MVC.
EricLaw: You're confused about how web security works, but in answer to your question, IE10 supports CORS for XHR.
I post a data with XDomainRequest but the server page (where i post the data) is not able to access the post data in case of IE8 and IE9. My code is:
data = JSON.stringify($("#freelisting").serialize()); var xdr = new XDomainRequest(); xdr.open("POST", url); xdr.send(data);
I already use header('Access-Control-Allow-Origin: *'); in my php file where i am posting this data.
[EricLaw]: How, exactly, does your server page attempt to access the data? As noted in the post, the request's Content-Type header will not contain application/json, so if your serverside framework expects it to, it will fail. You can use a tool like Fiddler to confirm that the data is being POSTed correctly.
Note that the response's Access-Control header only controls whether the client can read the response-- it has nothing to do with whether the POST data is sent.
Out of curiosity, why did you think moving away from the xml http request events was a good thing to do? Perhaps you should have left the onreadystatechange and have everything backward compatible. Not that it matters much these days.
[EricLaw] The ReadyStateChanged event model was confusing to developers, and the aim was to offer something simpler. Later versions of the XHR spec also added new events (e.g. "load", "progress") for the same reason.
We are trying to use http PUT method to send json data to server. Our server is modified with all the cors related headers as-response.addHeader("Access-Control-Allow-Origin", "*");response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, HEAD");response.addHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept");
When we try to use PUT method we are getting below error-No 'Access-Control-Allow-Origin' header is present on the requested resource. As 'Access-Control-Allow-Origin' header is alreay present with the service, we are not able to track what we missed. Also if we try to hit the same url with Rest Client we are able to see the result and all the headers are present.
[EricLaw] You posted this question on a blog about XDomainRequest, which, as explained, does not support PUT. However, we'll assume that you're using the CORS-capable XmlHttpRequest object in IE10+. The problem is that you need to respond to a HTTP OPTIONS "preflight" request, and it's likely that you're not doing so properly. Use Fiddler to watch the traffic between the client and the server; the OPTIONS request needs to carry the Access-Control-* response headers before the client will even send the PUT.