XDomainRequest - Restrictions, Limitations and Workarounds
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 https:// 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.
-Eric
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.
Comments
Anonymous
May 13, 2010
IIRC a cross-domain XMLHttpRequest could be built using postMessage to communicate with a cross-domain iframe which would relay the request. Use at your own risk, etc, etc.Anonymous
May 13, 2010
@Sean: Indeed, that's correct, but it requires that the other site offer an IFRAME that exposes this service, and it requires that your site trust that IFRAME not to do anything sneaky like navigate your top-level window to a malware-laden site, etc.Anonymous
July 12, 2010
I just hit the issue of trying to get the data posted from XDomainRequest from ASP.NET. Request.Form is, as Eric mentions, not populated because of ASP.NET's requirement for x-www-urlencoded as the contentType. So, here's the code I ended up writing using Request.InputStream: //set the header to support XDomainRequest Response.Headers.Add("Access-Control-Allow-Origin", "*"); System.IO.Stream str; String jsonContents; Int32 counter, strLen, strRead; // Create a Stream object. str = Request.InputStream; // Find number of bytes in stream. strLen = Convert.ToInt32(str.Length); // Create a byte array. byte[] byteArray = new byte[strLen]; // Read stream into byte array. strRead = str.Read(byteArray, 0, strLen); // Convert byte array to a text string. jsonContents = Encoding.UTF8.GetString(byteArray); Maybe this will save someone some time. :)Anonymous
September 14, 2010
Hi EricLaw, I've a question, i use XDR to call a url of other domain, using ajax , using XDR, this work fine on IE8, but How I implement XDR on IE7 and IE6? It's possible do calls to other domains since these browsers?Anonymous
September 14, 2010
The comment has been removedAnonymous
October 16, 2010
Ok so I'm trying to learn AJAX and it mentions a need to create an XMLHttpRequest object (I get and undefined error on IE8). So asked around and was suggested to use XDomainRequest I copied the example from the MSDN site and dumped it into an html page. The html tries to load data from an examplel domian to which I don't have access to. So I point the url to my localhost to read the data using GET. The script executes, creates the xdr object, does the send then the onerror function gets called. I don't have a clue as to why. Any help I'll put up the code if needed.Anonymous
October 16, 2010
@XDR: IE7 and IE8 absolutely support the native XMLHTTPRequest object; if they're not working, then your code was incorrect. The most likely explanation for your XDR not working is that you failed to send the Access-Control-Allow-Origin response header, or as mentioned in restriction #6 above, you tried to send the request from a page in the Internet Zone to a site (Localhost) in the IntrAnet zone.Anonymous
October 17, 2010
Ok so my test page has my XDR object url pointing to a file on the localhost. I don't have access to a remote site. How else can I get up to speed with XDR and AJAX? I'm using code from the MSDN XDR example.Anonymous
October 17, 2010
@XDR: You can use this URL www.enhanceie.com/.../streamWithPrelude.aspx as your target.Anonymous
August 22, 2011
Any chance custom header support was added in IE9?Anonymous
August 22, 2011
@Viraj: XDR deliberately does not support custom headers. No changes were made to XDR in IE9, except the one mentioned in the post (namely, InPrivate does not block XDR.) More generally, I update posts as things change, so unless you've got some contradictory information, asking: "Is this information correct?" is unnecessary.Anonymous
October 15, 2011
Why hasn't IE9+ made the change to allow custom headers? It's inconceivable to force developers to write APIs to support only plain/text as the Allow response. If i want JSON, i'll send application/json, if I want XML I'll send application/xml; unless the User is running IE? That's plain stupid. Consider that the internet is supposed to be services, that can intelligently respond based purely on headers.Anonymous
October 15, 2011
@Jason: I think you're a bit confused-- an XDomainRequest can return text in whatever format you like. Similarly, it can send text in whatever format you like. Now, to your question of "Why can't I send custom headers", you should probably read the blog post above, particularly point #3.Anonymous
November 02, 2011
In a comment above (16 Oct 2010 11:23 AM) you say "IE7 and IE8 absolutely support the native XMLHTTPRequest object". I tried all I can think of but all I get when trying to do a cross domain http post is access denied (same script works fine in chrome, ff, safari). Maybe you mean that XMLHTTPRequest works in the same domain only in IE... can you confirm?Anonymous
November 02, 2011
@Pietro: Of course that's what that means-- the developer in question was claiming that he was finding that window.XmlHttpRequest was returning undefined. XHR was invented by Microsoft in the late 1990s, and the same-origin restriction exists when the object is used in an untrusted context (like an Internet webpage). The idea of using CORS to permit cross-domain communication for XHR is a comparatively recent development, and for IE8 and IE9, we went a different, more secure route, using XDR. Long discussions of that decision are available, including a whitepaper by Sunava Dutta.Anonymous
December 12, 2011
I want to send cross domain request from javascript on IE and it seems that MS XMLHTTPRequest won't subject to cross domain policy. I was trying to use XDomainRequest to access google.com and the callback function loadd is never called. According to msdn, "The document will request data from the domain's server by sending an Origin header with the value of the origin. It will only complete the connection if the server responds with an Access-Control-Allow-Origin header of either * or the exact URL of the requesting document ". I used Fiddler to send auto response that include the Access-Control-Allow-Origin in the header and it workds. I look at the real response google returned and it does not include this in the header Does it really need the server to return repsonse with access-control-allow origin in the header in order to work? [EricLaw]: For security reasons, yes. If that's the case, how can we solve this problem if we send request to a server that is out of our control? [EricLaw]: For security reasons, you cannot; that's the point of the restriction-- the server must opt-in.Anonymous
January 24, 2012
It seems the request sent by IE doesn't have any content-type header. Being forced to user text/plain is not a problem in itself, but I was expecting IE to send a header like this one : content-type = text/plain but it's not there :-/ so I have to mess with my server implementation :-/Anonymous
February 08, 2012
I'm using XDR and responseText is populated. responseXML is not. The server is sending Content-type: text/xml It does have a BOM, however, on UTF-8 charset. Is XDR supposed to be automatically parsing and populating responseXML or not. One of your comments seems to indicate that an application can send whatever it likes, which only muddied the question for me. I guess for now, I'll fire up DOMParser and feed in the XML and pray.Anonymous
February 08, 2012
The comment has been removedAnonymous
April 08, 2012
It is not clear for me: is the CORS header needed if the request is done to the same domain? The spec seems to say that the CORS header is mandatory. This would means that we need to determine if a request is to another domain and choose between XMLHttpRequest and XDomainRequest...Anonymous
May 09, 2012
For some reason when I open the XDomainRequest on a clients computer it does not work, it works on their servers but not their local machines. Is there an IE setting I need to change? The exception is called TypeCast Error, it happens when this.xdr.open("POST", EndpointAddress); is called, and only on their login computers. Any ideas why? function CreateCrossDomainMessage(ExecuteExternalWebServiceClass, EndpointAddress, PostData) { new CrossDomainMessage(ExecuteExternalWebServiceClass, EndpointAddress, PostData); } function CrossDomainMessage(ExecuteExternalWebServiceClass, EndpointAddress, PostData) { this.ExecuteExternalWebServiceClass = ExecuteExternalWebServiceClass; this.xdr = new XDomainRequest(); XDomainRequest.prototype.CrossDomainMessage = this; this.CrossDomainMessageLoaded = function () { this.CrossDomainMessage.ExecuteExternalWebServiceClass.MessageReady(this.CrossDomainMessage.xdr.responseText); } this.CrossDomainMessageError = function () { this.CrossDomainMessage.ExecuteExternalWebServiceClass.MessageError(); } this.CrossDomainMessageTimeout = function () { this.CrossDomainMessage.ExecuteExternalWebServiceClass.MessageTimeout(); } this.xdr.open("POST", EndpointAddress); this.xdr.timeout = 20000; this.xdr.ontimeout = this.CrossDomainMessageTimeout; this.xdr.onerror = this.CrossDomainMessageError; this.xdr.onload = this.CrossDomainMessageLoaded; this.xdr.send(PostData); }Anonymous
June 19, 2012
Eric, Can you give more detail explanation on how to implement postMessage-Proxy-for-XDR (or maybe some code example)? my application is invoked from customer web site, so I have no control on what http protocol they use. Second, there seems timing issue on the onload event, sporadically it is fired too early (before server send back response). Any suggestion to workaround?Anonymous
June 19, 2012
@EricChang: The link I provided in my blog post is the code sample. Just View Source.Anonymous
June 21, 2012
Hi EricLaw, We are developing a webservices based system, and we are using CORS to change data between diferent domains. But a particular behavior we presenced at IE 8 and IE 9, using XDomainRequest, is reporting a poor performance when consuming considerable data volume. For exemple: when handling 500Kb of data lenght (allways string data, text based info), IE works pretty fine. When handling 2.8Mb data lenght, both IE versions still going to work good. But then, as an application behavior, sometimes large data will be loaded: 6Mb, 14Mb, and even 20Mb. At those points, performance goes down increasily, and as bigger are the data, slower is the transfer time. Sometimes, Windows OS points IE hangs up (what is not true: in fact, if you stay wainting for the response "onload" event, it will be fired, at some time). So, i would like to know if this issue is a knowlegment, someone else have this problem too, and if is there something that whe can do to make big data responses faster when using XDomainRequest. Note: at other browsers, XMLHttpRequest Level 2 based, this behavior does not happen. Thank you FwendtAnonymous
September 27, 2012
How do I get more error details when onerror callback is called?Anonymous
December 04, 2012
I am facing a serious problem with XDomainRequest POST, Can you please let me know how can i set my reuest's body like this? I m trying to post some data into AMAZON S3 server. This POST body also contains a file object. Please help!!! -----------------------------98942870323811 Content-Disposition: form-data; name="policy" eyJleHBpcmF0aW9uIjogIjIwMTItMTItMTJUMDA6MDA6MDBaIiwgImNvbmRpdGlvbnMiOiBbICB7ImJ1Y2tldCI6ICJ1cGxvYWRzLW1vZHJpYS1jb20ifSAsWyJzdGFydHMtd2l0aCIsICIka2V5IiwgInVwbG9hZHMvIl0sIHsiYWNsIjogInByaXZhdGUifSwgWyJzdGFydHMtd2l0aCIsICIkQ29udGVudC1UeXBlIiwgIiJdLCBbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwgMCwgMjA5NzE1MjBdIF0gfQ== -----------------------------98942870323811 Content-Disposition: form-data; name="signature" XXXXXXXXXXXXXXXXXXXX -----------------------------98942870323811 Content-Disposition: form-data; name="Content-Type" application/octet-stream -----------------------------98942870323811 Content-Disposition: form-data; name="file"; filename="raee.txt" Content-Type: text/plain EricLaw: Sorry, you won't be able to use XDR for this, due to problem #4. Multipart-MIME uploads require that you set the proper Content-Type header with the boundary marker. XDR forbids that.Anonymous
January 28, 2013
The comment has been removedAnonymous
January 30, 2013
Eric, you answered my comment/issue (Art 29-Jan) telling me to put in Access-Control-Allow-Origin header but that's the first thing I tried. That is not the problem here. IE is failing to accept the fact that a CSS file hosted on AWS and downloaded using the CORS protocol (Access-Control-Allow-Origin header) and then is asked to load a font via @font-face from the same place on AWS. The CSS file is downloaded and used just fine but the font file requested in that CSS file is not being accessed. There is some quirk in IE that doesn't handle this situation (I've tested IE 9,10). EricLaw: Sorry, no. This configuration works fine and is used by thousands of sites every day. Feel free to email me if you'd like me to debug it for you.Anonymous
February 14, 2013
The comment has been removedAnonymous
February 16, 2013
The comment has been removedAnonymous
April 08, 2013
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.Anonymous
April 10, 2013
@Louis: Email me a Fiddler capture and I'll have a look. Are you positive the Zone settings are what you think they are?Anonymous
April 17, 2013
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?Anonymous
April 27, 2013
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.Anonymous
May 23, 2013
All browser except MS IE (any version) run a perfect CORS XHR without any error. If you set header content to text/plain do not run any javascript comes from the remote source. How long the MS IE will be the black sheep??? All the time msie going against the compliances EricLaw: You're confused about how web security works, but in answer to your question, IE10 supports CORS for XHR.Anonymous
January 22, 2014
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.Anonymous
June 28, 2014
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.Anonymous
July 29, 2014
The comment has been removedAnonymous
November 02, 2014
Hi Eric, I am trying to do XDomain Request JSON POST call. var xdr = new XDomainRequest();
xdr.open("POST", url);
xdr.onprogress = function () { };
xdr.ontimeout = function () { };
xdr.onload = function() { alert('data '+JSON.parse(xdr.responseText)); }
setTimeout(function () {xdr.send(JSON.stringify(data));}, 0); If is see Response status is 200 Ok. But response body coming as error like below.
{"errorCode":-1,"errorMsg":"Content type 'application/octet-stream' not supported"} Can you please help how i can resolve this issue. [EricLaw] This error suggests that the server requires a Content-Type header on the request. Because XDomainRequest cannot send such a header, the server will need to be changed to accommodate the missing request header, or the XDomainRequest object cannot be used.Anonymous
November 26, 2014
Hi Eric, i am using XDomainRequest POST to send a request to cross-domain server. But i am getting " Bad Request 400"error . Can you please help in resolving this. [EricLaw] You can use Fiddler to confirm, but I'll bet that the target server demands a Content-Type: application/json request header.Anonymous
November 26, 2014
should this be set on the server side since xdomainrequest doesn't send a header? [EricLaw] I don't know what you mean by "set on the server side" but for XDomainRequest to work for you, your server will have to accept a POST without a Content-Type header...Anonymous
January 16, 2015
After reading your post I tried using XHR in my code and removed XDR. When I used XDR the service call was working fine but as soon as I used XHR it started giving access denied error. By default my aspx pages render in IE10 document mode but as you suggested to use XHR it gives access denied in IE10 and in IE11 edge mode. Why does XDR works and XHR gives access denied ? And if it is because of security zones I cannot ask every use to add my site to the security zone for service to work and why is XDR ignoring security zones and XHR not ?Anonymous
February 09, 2015
I'm using the XDocumentRequest object to post information from one site to another to authenticate a users (I'm building a login page). In addition to some of limitations discussed. I've noticed that in the response from my server I'm unable to set the authentication cookie. Basically the XDocumentRequest won't sent cookies in the current domain that are included in the response from another site. Is this by design? Is there a workaround?Anonymous
February 09, 2015
@Robert: As noted in the article, XDomainRequest "will ignore any authentication challenges or Set-Cookie directives in the HTTP response"