How to Deal with same-origin policy restriction in angularjs

Web browsers enforce the same-origin security policy. This policy authorizes XHR interactions only with resources originating from the same source (defined as a combination of a protocol, host and its port) and enforces restrictions on interactions with “foreign” resources.

As web developers, we need to constantly balance security considerations with functional requirements to aggregate data from multiple sources. Indeed, it is often desirable to fetch data from third party services and present those data in our web applications. Unfortunately, XHR requests can’t easily reach servers outside of the source domain unless we play some tricks.

There are several techniques for accessing data exposed by external servers: JSON with padding (JSONP) and Cross-origin resource sharing (CORS) are probably the most popular ones in the modern web. This section shows how angularjs helps us applying those techniques in practice.

Overcoming same-origin policy restrictions with JSONP

Using JSONP is a trick that allows fetching data by passing the same-origin policy restrictions. It relies on the fact that browsers can freely pull JavaScript from foreign servers by using the <script> tag.

JSONP calls don’t trigger XHR requests but instead generate a <script> tag whose source points to an external resource. As soon as a generated script tag appears in the DOM a browser performs its duty and calls the server. The server pads the response with a function invocation (thus the “padding” in the name of JSONP technique) in the context of our web application.

Let’s examine a sample JSONP request and response to see how it works in practice. First of all we need to invoke a JSONP request:

$http
  .jsonp('http://angularjs.org/greet.php?callback=JSON_CALLBACK', {
    params:{
      name:'World'
    }
  }).success(function (data) {
    $scope.greeting = data;
  });

Upon invoking the $http.jsonp method angularjs will dynamically create a new <script> DOM element like:

<script type="text/javascript" src="http://angularjs.org/greet.php?callback=angular.callbacks._k&name=World"></script>

As soon as this script tag is attached to the DOM the browser will request the URL specified in the src attribute. The response, upon arrival, will have a body following a pattern like:

angular.callbacks._k ({"name":"World","salutation":"Hello","greeting":"Hello World!"});

A JSONP response looks like a regular JavaScript function call and in fact this exactly what it is. angularjs generated the angular.callbacks._k function behind the scenes. This function, when invoked, will trigger a success callback invocation. The URL supplied to the $http.jsonp function call must contain the JSON_CALLBACK request parameter. angularjs will turn this string into a dynamically generated function name.

JSONP limitations

JSONP is a smart and useful work-around for the same-origin policy restrictions but it has several limitations. Firstly, we should only GET HTTP requests can be issued using the JSONP technique. Error handling is also very problematic, since browsers won’t expose HTTP response status from the <script> tag back to JavaScript. In practice it means that it is rather difficult to reliably report the HTTP status errors and invoke error callbacks.

JSONP also exposes our web application to several security threats. Apart from the well-known XSS attack, probably the biggest issue is that a server can generate any arbitrary JavaScript in the JSONP response. This JavaScript will be loaded to a browser and executed in the context of a user’s session. A server configured in a malicious way could execute undesired scripts causing different damages, ranging from simply breaking a page to stealing sensitive data. As such, we should be really careful while selecting services targeted by JSONP request and only use trusted servers.

Recommended :  How to create a RESTful web service with Yii 2

Overcoming same-origin policy restrictions with CORS

Cross-origin resource sharing (CORS) is a W3C specification that aims at solving the same problem as JSONP in a standard, reliable, and secure way. The CORS specification builds on top of the XMLHttpRequest object to enable the cross-domain AJAX requests in a well-defined and controlled manner.

The whole idea behind CORS is that a browser and a foreign server need to coordinate (by sending appropriate request and response headers) to conditionally allow cross-domain requests. As such, a foreign server needs to be configured properly. Browsers must be able to send appropriate requests, and headers, and interpret server responses to successfully complete cross-domain requests.

Note

A foreign server must be configured properly to participate in a CORS conversation. Those who need to configure servers to accept HTTP CORS can find more information in http://www.html5rocks.com/en/tutorials/cors/. Here we are going to focus on the browser role in the whole communication.

CORS requests are roughly divided into “simple” and “non-simple” ones. GET, POST, and HEAD requests are considered as “simple” (but only when sending a subset of allowed headers). Using other HTTP verbs or request headers outside of the allowed set will force a browser to issue a “non-simple” CORS request.

Note

Most of the modern browsers support CORS communication out of the box. Internet Explorer in its Version 8 and 9 enables CORS support only with the non-standard XDomainRequest object. Due to limitations of the IE-specific XDomainRequest implementation angularjs doesn’t provide support for it. As a result, the CORS requests are not supported with the $http service on IE 8 and 9.

With non-simple requests, the browser is obliged to send a probing (preflight) OPTION request and wait for the server’s approval before issuing the primary request. This is often confusing, since a closer inspection of the HTTP traffic reveals mysterious OPTIONS requests. We can see those requests by trying to call the MongoLab REST API directly from a browser. As an example, let’s inspect the HTTP communication while deleting a user:

$http.delete('https://api.mongolab.com/api/1/databases/ascrum/collections/users/' + userId,
  {
    params:{
      apiKey:'4fb51e55e4b02e56a67b0b66'
    }
  }
);

We can see two requests (OPTIONS and DELETE) targeting the same URL:

Overcoming same-origin policy restrictions with CORS

The response from the MongoLab server includes headers that make the final DELETE request possible:

Overcoming same-origin policy restrictions with CORS

The MongoLab servers are well configured to send appropriate headers in response to the CORS request. If your server is not properly configured the OPTIONS request will fail and the target request won’t be executed.

Server-side proxies

JSONP is not an ideal technique for making cross-origin requests. The CORS specification makes the situation better, but it still requires additional configuration on the server side and a browser that supports the standard.

If you cannot use CORS or JSONP techniques, then there is always the option of avoiding cross-domain request issues altogether. We can achieve this by configuring a local server as a proxy to a foreign one. By applying a correct server configuration we can proxy cross-domain requests through our server, and thus have the browser target only our servers. This technique works on all browsers, and doesn’t require pre-flight OPTIONS request. Also, it doesn’t expose us to any additional security risks. The downside of this approach is that we need to configure the server accordingly.

Note

The sample SCRUM application described in this book relies on the node.js server configured in a way that it proxies calls to the MongLab REST APIs.

About the author

Deven Rathore

I'm Deven Rathore, a multidisciplinary & self-taught designer with 3 years of experience. I'm passionate about technology, music, coffee, traveling and everything visually stimulating. Constantly learning and experiencing new things.

  • Lakmal Caldera

    Yet another way to overcome the same origin policy and I think by far the simplest is using a proxy server on the client end. If you are working with a build tool like grunt or gulp, this is even easier and several plugins that support this. Basically create a Nodejs proxy server and have it listen all your request made to a particular url and intercept them all. If you want to redirect the url to a different domain, use the proxy server to stop the url request to the original server and rewrite and redirect the url to the new domain. Once the data is fetched from the new domain, serve the response to the app as if it was the response from the originally intercepted request. Hope that help! 🙂

    • Deven Rathore

      good one

  • bripkens

    Thanks for mentioning this. I have seen quite a bunch of people adding security holes to their applications only to avoid SOP. This had me frustrated and I wrote a tool so that engineers would stop doing this. It works by having proxies during development.

    https://github.com/bripkens/proxrox

    • Deven Rathore

      good work

Pin It on Pinterest

Shares