Search code examples
ajaxhttpresthttp-headerscsrf

Origin and Host headers for same domain requests


We have RESTful JSON endpoints serving AJAX requests, with the desire to support Cross Origin Resource Sharing. We are locking things down to ensure that we don't have to worry about Cross Site Request Forgery (CSRF) attacks. Part of the approach we are using is to check for the presence of a Origin header and verify that it is included in a whitelist of approved Origins. However, we have noticed that some browsers (Chrome and Safari among them) include the Origin header with AJAX POST requests, even when originating from the same domain (so not a CORS request).

Since we would prefer to not require our users to have to whitelist the same domain that the REST endpoints are being served from, we would like to determine automatically whether or not a given request is "Same Domain" or "Cross Origin". To do this, right now our best attempt is to watch for the presence of an Origin header, and if it exists, compare it with the value of the Host header. Our logic is that if the Origin matches the Host, then this request must be "Same Domain" and so we do not need to check for the Origin in the whitelist.

Here is a snippet of the server-side JS code we are thinking of using to accomplish this:

     if (typeof (headers["Origin"]) !== "undefined" &&
         headers["Origin"].replace(new RegExp("^.*?//"), "") !== headers["Host"] &&
         !contains(allowedOrigins, headers.Origin) ) {
         return false;
     } else {
         return true;
     }

The regexp comparison is needed because Origin header will come in looking like this:

http://localhost:8080

Whereas the Host header will come in looking like this:

localhost:8080

So I use that regexp to strip off the leading http://.

The problem is that we haven't seen any other implementations using or discussing this approach. This concerns us that maybe this isn't an appropriate method, for some reason. So, the question is - is comparing the Host header with the Origin header a safe way to determine if a request originates from the same domain?

Also, is removing the leading protocol:// from the Origin as I've shown going to always yield the proper value in relation to Host?


Solution

  • I would advice you to instead check for the Ajax header: X-Requested-With: XMLHttpRequest

    Same-origin Ajax can add custom headers and almost all popular frameworks such as jQuery adds X-Requested-With: XMLHttpRequest.

    For CORS however, custom headers provoke a pre-flight HTTP OPTIONS request.

    Thus, if you see X-Requested-With: XMLHttpRequest (or any other custom header) without a pre-flight you know it's a same-origin Ajax call.