Check if same origin policy applies
Asked Answered
I

5

25

Is there a "safe" way to check if the same origin policy applies to an URL before actually trying to use ajax methods? Here is what I have:

function testSameOrigin(url) {

    var loc = window.location,
        a = document.createElement('a');

    a.href = url;

    return a.hostname == loc.hostname &&
           a.port == loc.port &&
           a.protocol == loc.protocol;
}

This sort of works, but it’s kind of a manual guess based on the wikipedia article. Is there a better way of pre-checking cross domain allowance? jQuery is OK to use.

Icecap answered 22/2, 2012 at 23:17 Comment(1)
If you do a cross domain request it will fail with readystate=4 and statuscode=0, which is similar to an aborted request. Since you need to guard against aborted requests anyway why would you need this check? I feel like this security measure is enforced from outside, you have no control over it, so any check from within the environment is by definition wrong. So I don't think you should ever check for it, just let the request fail.Distended
F
8

Interesting question! I searched around and couldn't find anything other than what you posted, but I did come across this when I was messing around with some test code. If you just want a simple way to test a URL without making a request, I'd do it the way you're doing it. If you don't care about making a request to test, you could try this:

Make a simple ajax request to whatever URL you want:

var ajaxRequest = $.ajax({
  url: 'http://www.google.com',
  async: false
});

which returns a jqXHR object, which you can then check:

ajaxRequest.isRejected(); // or...
ajaxRequest.isResolved();

Now, the only problem with this is that isRejected() will evaluate to true for every single case where the page doesn't load (i.e. 404 Not Found, etc.), but you can check the status code with:

ajaxRequest.status;

It looks like the above line will return 0 when you attempt to break the same origin policy, but it will return the appropriate error code (again, i.e. 404) in other cases.

So to wrap up, maybe you could try doing something like:

function testSameOrigin(testUrl) {

  var ajaxRequest = $.ajax({
    url: testUrl,
    async: false
  });

  return ajaxRequest.isRejected() && ajaxRequest.status === 0;
}

Not a definitive answer by any means, but I hope it helps you figure out what you're looking for!

Fawne answered 29/2, 2012 at 22:28 Comment(2)
Yes, that’s an interesting approach. In my original case, I wanted to get rid of the nasty console errors you get when doing the rejected ajax call by trying to "validate" the URL. I also just recently discovered that doing an ajax requests on a local file (the file: protocol) also fails every time. So that is another case to check for...Icecap
You can use ajax for local files by setting the isLocal flag to true api.jquery.com/jQuery.ajax/#jQuery-ajax-settingsFawne
S
16

Is there a "safe" way to check if the same origin policy applies to an URL before actually trying to use ajax methods? Here is what I have:

function testSameOrigin(url) {

    var loc = window.location,
        a = document.createElement('a');

    a.href = url;

    return a.hostname == loc.hostname &&
           a.port == loc.port &&
           a.protocol == loc.protocol;
}

This is a safe and reliable way of doing it, provided you are doing (or rather not doing) certain things.

This sort of works, but it’s kind of a manual guess based on the wikipedia article.

This should fully work under the "normal" circumstances. It will need to be modified if you are planning to use cross-domain scripting.

If you modify document.domain in your scripts, for example from "foo.example.com" and "bar.example.com" to "example.com" your testSameOrigin function would return false for "http://example.com", where in fact it should return true.

If you are planning on modifying document.domain, you can add simply add a check for that in your script.

If you are planning on using CORS (see the link above) to allow cross-domain communication, it will also return a false negative. But if you are using CORS, you will have a list of domains that you can communicate with, and you can add that list to this function as well.

Is there a better way of pre-checking cross domain allowance? jQuery is OK to use.

Probably not, although it may be worth mentioning that what you are seeing in the console from Steve's answer might be the "observer's dilemma" ... Those errors look like they are resulting from the console trying to inspect the other window, not necessarily from the script.

Assuming you're not messing with document.domain or using CORS, your original solution is probably better, as it doesn't need to make an extra request to determine whether the server is available or not. Even if you are doing some cross-domain scripting, modifying the function you have now to accommodate it is probably your best bet.

Snakeroot answered 5/3, 2012 at 16:2 Comment(4)
while this is a good way to check for CORS requests it breaks on IE7 while the anchor generated won't return any information about the link so for example evaluating a link such <a href='main.html'></a> in IE7 it will return a.href => "main.html" a.hostname => "" etc. I am still trying to figure out how to deal with this in IE7 tho. but couldn't find any good solution. since it will never create the full href.Lemmie
What document.domain check would you add? Would simply replacing loc.hostname with document.domain be correct?Yon
@BT a.hostname would still be "foo.example.com" and document.domain would just be "example.com". It would have to be a special-case check, i.e. if you know you are rewriting both document.domains to "example.com", the hostname check would be something like a.hostname == loc.hostname || a.hostname == "foo.example.com"Snakeroot
I've encountered a case where a.port != loc.port because the first one comes up as 80 and the second as blank.Gurrola
F
8

Interesting question! I searched around and couldn't find anything other than what you posted, but I did come across this when I was messing around with some test code. If you just want a simple way to test a URL without making a request, I'd do it the way you're doing it. If you don't care about making a request to test, you could try this:

Make a simple ajax request to whatever URL you want:

var ajaxRequest = $.ajax({
  url: 'http://www.google.com',
  async: false
});

which returns a jqXHR object, which you can then check:

ajaxRequest.isRejected(); // or...
ajaxRequest.isResolved();

Now, the only problem with this is that isRejected() will evaluate to true for every single case where the page doesn't load (i.e. 404 Not Found, etc.), but you can check the status code with:

ajaxRequest.status;

It looks like the above line will return 0 when you attempt to break the same origin policy, but it will return the appropriate error code (again, i.e. 404) in other cases.

So to wrap up, maybe you could try doing something like:

function testSameOrigin(testUrl) {

  var ajaxRequest = $.ajax({
    url: testUrl,
    async: false
  });

  return ajaxRequest.isRejected() && ajaxRequest.status === 0;
}

Not a definitive answer by any means, but I hope it helps you figure out what you're looking for!

Fawne answered 29/2, 2012 at 22:28 Comment(2)
Yes, that’s an interesting approach. In my original case, I wanted to get rid of the nasty console errors you get when doing the rejected ajax call by trying to "validate" the URL. I also just recently discovered that doing an ajax requests on a local file (the file: protocol) also fails every time. So that is another case to check for...Icecap
You can use ajax for local files by setting the isLocal flag to true api.jquery.com/jQuery.ajax/#jQuery-ajax-settingsFawne
M
3

Try this solution as well.

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

function sameOrigin(url) {
  // test that a given url is a same-origin URL
  // url could be relative or scheme relative or absolute
  var host = window.document.location.host; // host + port
  var protocol = window.document.location.protocol;
  var srOrigin = '//' + host;
  var origin = protocol + srOrigin;
  // Allow absolute or scheme relative URLs to same origin
  return (url === origin || url.slice(0, origin.length + 1) === origin + '/') ||
    (url === srOrigin || url.slice(0, srOrigin.length + 1) === srOrigin + '/') ||
    // or any other URL that isn't scheme relative or absolute i.e relative.
    !(/^(\/\/|http:|https:).*/.test(url));
}

// if you want to check before you make a call
if (!csrfSafeMethod(data.type) && sameOrigin(data.url)) {
  // ...
}

// or if you want to set csrf token
$.ajax({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
      xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
    }
  }
});
Moua answered 26/1, 2015 at 16:22 Comment(0)
G
0

Another way to execute cross domain script is using JSON-P. You can also read this article. Otherwise, the cross domain scripting is not allowed by the same origin policy.

Gony answered 28/2, 2012 at 23:50 Comment(1)
I’m not sure why all answers is about how to make or not make cross-browsers ajax requests, that is not at all what I am asking... Do I need to be more clear?Icecap
Y
0

Building off of Dagg Nabbit's answer, this seems a little more complete:

function sameOrigin(url) {
    var loc = window.location, a = document.createElement('a')
    a.href = url

    return a.hostname === loc.hostname &&
           a.port === loc.port &&
           a.protocol === loc.protocol &&
           loc.protocol !== 'file:'
}

Caveats I can think of:

Yon answered 7/7, 2014 at 20:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.