CORS in Action: Creating and consuming cross-origin APIs (2015)
Appendix D. Other cross-origin techniques
This appendix covers alternatives to CORS. Before CORS, the techniques described here could be used to make cross-origin requests. While CORS is the standard for modern web applications, these alternative techniques are useful for making cross-origin requests from older browsers.
Each technique follows a similar pattern, as illustrated in figure D.1. The client wants to make a request to a server that lives at a different origin. In between there is a proxy mechanism that processes the request from the client and sends it to the server.
Figure D.1. All cross-origin techniques follow a similar pattern.
figure D.1 will reappear in each section that follows to help highlight how each technique operates.
D.1. JSONP
JSONP, which stands for JSON with padding, uses the browser’s own script tag to send cross-origin requests, as shown in figure D.2.
Figure D.2. JSONP uses a <script> tag to send cross-origin requests.
The following listing shows how to use JSONP to recreate the Flickr example from chapter 1.
Listing D.1. Using JSONP to access Flickr
In this listing, the XMLHttpRequest is replaced with a <script> tag that initiates a request to the Flickr API server. The <script> tag can be created either by embedding a <script> tag in the HTML, or by dynamically creating it with JavaScript (by using document.createElement('script')). A <script> tag traditionally points to a file that contains JavaScript code. But in this case, the Flickr API server responds with code that wraps the API response in a JavaScript function. When the browser receives the response, it executes the JavaScript code, which in turn executes the callback function. figure D.3 shows what a typical JSONP flow looks like.
Figure D.3. Making a cross-origin request using JSONP
The key to a successful JSONP request is the callback function. When initiating a JSONP request, the <script> tag has a parameter set to the name of the callback function. In figure D.3, the callback function is namedloadPhotos. When sending a response back to the client, the server first pads the response with the name of the callback function (this is where the word padding in JSON with padding comes from). For example, if the API response looks like this:
{ "photos": { "page": 1, "photo": [...] } }
JSONP would wrap the response in a callback, like this:
loadPhotos({ "photos": { "page": 1, "photo": [...] } });
Finally, when the browser receives the server’s response, it actually calls the callback function with the data provided by the server. In this example, the loadPhotos function defined by the client gets called with the API response data.
JSONP is one of the oldest cross-origin techniques out there, and it enjoys wide browser support. But because it uses a <script> tag, JSONP can only support GET requests. There is no way to make a POST, PUT, or DELETE request using JSONP. If the browser has any cookies from the server, those cookies will be included on the request. This can open up the API to cross-site forgery requests.
JSONP is ideal for sharing publicly available data. The preceding Flickr example is a perfect use case for JSONP: the images are publicly available, and the sample is only displaying the images on the page. Due to the cross-site forgery concerns, JSONP shouldn’t be used to make updates to data, or to share sensitive user data. Finally, because the server is essentially executing arbitrary JavaScript in the user’s browser, it’s important that the client fully trust the response from the server.
D.2. Flash
Adobe’s Flash is a software platform that brings rich, interactive applications to the browser. Flash applications are written in a language called ActionScript, which can make cross-origin requests to a remote server, as shown infigure D.4.
Figure D.4. Flash applications have native support for cross-origin requests.
Similar to CORS, Flash requires the server to grant permission to clients making cross-origin requests. Except instead of HTTP headers, Flash uses an XML file that lives on the server. This XML file is named crossdomain.xml, and it specifies rules such as which origins are allowed to make cross-origin requests, and which headers are allowed. The following code shows an example of the crossdomain.xml file from the Flickr API server.
<cross-domain-policy>
<allow-access-from domain="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="Authorization"/>
<site-control permitted-cross-domain-policies="master-only"/>
</cross-domain-policy>
The file in the preceding code indicates that any origin can make cross-origin requests (domain="*"), regardless of whether or not the origin supports SSL (secure="false"). The Authorization request header is allowed (headers="Authorization") from all origins (domain="*"). A full specification of crossdomain.xml is available at www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html.
Figure D.5 shows the flow of a cross-origin request from Flash. Before issuing a cross-origin request, Flash first reads the crossdomain.xml file . If the policy maps to the client issuing the request, Flash sends the request to the server
.
Figure D.5. Making a cross-origin request using Flash
Flash works best in cases where you need rich HTTP support, but you also need deep browser support. Like JSONP, Flash has been around for a long time, and it enjoys wide support across desktop browsers. It’s more powerful than JSONP because it allows different HTTP methods. And like CORS, Flash puts the servers in charge of how cross-origin requests behave by using the crossdomain.xml file.
Using Flash requires that developers learn and use ActionScript. And while Flash enjoys strong desktop support, it has limited support on mobile devices (while some Android phones support Flash, all iPhones and iPads do not).
D.3. postMessage and easyXDM
HTML5’s Web Messaging API defines the postMessage method, which allows documents from different origins to communicate with each other, as shown in figure D.6. Using postMessage, a page can send an HTTP request to an <iframe> HTML tag embedded on the page, and the <iframe> tag can make an XMLHttpRequest to its own server.
Figure D.6. postMessage allows two <iframe> tags from different origins to communicate.
Because the <iframe> code and the server are from the same origin, the XMLHttpRequest is a same-origin request. Figure D.7 shows the request flow when using postMessage.
Figure D.7. Making a cross-origin request using postMessage
The postMessage spec was implemented before the CORS spec, so for a brief time period there were browsers that supported postMessage but didn’t support CORS. Internet Explorer 8, Internet Explorer 9, Firefox 3.0, and Opera versions less than 12 don’t fully support CORS but do support <iframe>-to-<iframe> communication via postMessage. As you saw in chapter 2, Internet Explorer 8 and Internet Explorer 9 have limited support for CORS, so if those browsers are important to you, the postMessage technique may be a good alternative.
An alternative option is the easyXDM library, which can be found at http://easyxdm.net/. The easyXDM library provides a consistent interface for making cross-origin requests across all browsers. The library uses the postMessagetechnique for browsers that support postMessage, and falls back on other techniques (such as Flash) for browsers that don’t support postMessage. The easyXDM library handles the complex details of making a cross-origin request so you don’t have to.
D.4. Server-side request
Unlike JavaScript code running in a browser, code running on a server isn’t constrained by the same-origin policy. If none of the other techniques work for your cross-origin needs, you can always write server-side code to make a cross-origin request, as shown in figure D.8.
Figure D.8. Server-side code isn’t constrained by the browser’s same-origin policy.
Server code can make a request to any web page, regardless of whether that web page has CORS headers or not. Figure D.9 shows what a request flow looks like when using a server to make cross-origin requests:
Figure D.9. Making a cross-origin request using a server-side proxy
1. The client initiates the request by making an XMLHttpRequest to the client’s own web server. This is different from CORS, where the XMLHttpRequest was made directly to the remote server .
2. The client’s server makes a request to the API server and receives the response .
3. The client’s server forwards the response back down to the client’s JavaScript code. Because the request from the client’s JavaScript code to the client’s server all takes place on the same origin, there is no need for CORS headers on the response .
Making server-side requests can enable almost any type of cross-origin request. Because this is such a powerful technique, remote servers may not look too kindly on this technique, and may even take steps to block clients from making these types of requests. If you make cross-origin requests from the server, be sure to get the remote server’s permission to do so. The one thing server-side requests can’t do is include cookies from the remote origin. If the remote origin saved any cookies in the user’s browser, there is no way for the client JavaScript code to read those cookies and include them on the request.