Check if same origin policy applies - javascript

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.

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.

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!

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"));
}
}
});

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.

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:
doesn't take document.domain into account
doesn't take CORS into account
Doesn't work in IE7 (as mentioned by zanona)
Doesn't work in weird environments (like android) where you can access arbitrary file:// protocol paths (someone please verify this, the info about android might be outdated https://security.stackexchange.com/questions/25138/same-origin-policy-for-file-urls-in-android-browser)

Related

how to protect location.href from cross site scripting in javascript?

Here in my javascript function im using location.href as follows
location.href = "../Floder1/result.jsp"; it is working fine but when i used fortify tool it is showing Cross-site Scripting which can result in the browser executing malicious code. how to protect this from cross site scripting. Thank you very much,your answer will be very much appreciated.
This code should work only in firefox since Proxy isn't implemented in all browsers
What you can do is to replace the original location object with a proxied one where you add some logic to your proxy to check for allowed value for location. this will not protect against the direct modification of the original object (location) but if you use only the proxied object in your code you should be fine.
// suppose we are in example.com
let validator = {
set: function(obj, prop, val) {
if (prop === 'href') {
if(typeof val != 'string'){
throw new TypeError('href must be string.');
}
if (!val.startsWith("https://example.com/")) {
throw new Error('XSS');
}
}
obj[prop] = val;
return true;
},
get: function(obj, prop){
return prop in obj?
obj[prop] :
null;
}
};
let proxiedLocation = new Proxy(location, validator);
console.log(proxiedLocation.href);// work same as location.href
proxiedLocation.href = "https://example.com/page1";// work fine
proxiedLocation.href = "https://example.net/page1";// cause exception
The Cross-site Scripting occurs when the user can put data in the webpage or get session data for example.
HOW PROTECT
You never allow inject code in your webpage. So, if you have a form, check it in the server and parse it before print in your page.
You shouldn't allow that the page content is changed by the href. You always escape the data before!.
Read this answer about location.href: https://stackoverflow.com/a/24089350/2389232
SAMPLE:
You have a iframe what changes with a GET variable:
sample.tld/index.jsp?iframe=none.jsp
I can inject a script to your iframe so you should protect it with escape characters:
// Escape the characters in the server and send it to the client.
// So the variable GET iframe will be valid

Is it possible to control Firefox's DNS requests in an addon?

I was wondering if it was possible to intercept and control/redirect DNS requests made by Firefox?
The intention is to set an independent DNS server in Firefox (not the system's DNS server)
No, not really. The DNS resolver is made available via the nsIDNSService interface. That interface is not fully scriptable, so you cannot just replace the built-in implementation with your own Javascript implementation.
But could you perhaps just override the DNS server?
The built-in implementation goes from nsDNSService to nsHostResolver to PR_GetAddrByName (nspr) and ends up in getaddrinfo/gethostbyname. And that uses whatever the the system (or the library implementing it) has configured.
Any other alternatives?
Not really. You could install a proxy and let it resolve domain names (requires some kind of proxy server of course). But that is a very much a hack and nothing I'd recommend (and what if the user already has a real, non-resolving proxy configured; would need to handle that as well).
You can detect the "problem loading page" and then probably use redirectTo method on it.
Basically they all load about:neterror url with a bunch of info after it. IE:
about:neterror?e=dnsNotFound&u=http%3A//www.cu.reporterror%28%27afew/&c=UTF-8&d=Firefox%20can%27t%20find%20the%20server%20at%20www.cu.reporterror%28%27afew.
about:neterror?e=malformedURI&u=about%3Abalk&c=&d=The%20URL%20is%20not%20valid%20and%20cannot%
But this info is held in the docuri. So you have to do that. Here's example code that will detect problem loading pages:
var listenToPageLoad_IfProblemLoadingPage = function(event) {
var win = event.originalTarget.defaultView;
var docuri = window.gBrowser.webNavigation.document.documentURI; //this is bad practice, it returns the documentUri of the currently focused tab, need to make it get the linkedBrowser for the tab by going through the event. so use like event.originalTarget.linkedBrowser.webNavigation.document.documentURI <<i didnt test this linkedBrowser theory but its gotta be something like that
var location = win.location + ''; //I add a " + ''" at the end so it makes it a string so we can use string functions like location.indexOf etc
if (win.frameElement) {
// Frame within a tab was loaded. win should be the top window of
// the frameset. If you don't want do anything when frames/iframes
// are loaded in this web page, uncomment the following line:
// return;
// Find the root document:
//win = win.top;
if (docuri.indexOf('about:neterror') == 0) {
Components.utils.reportError('IN FRAME - PROBLEM LOADING PAGE LOADED docuri = "' + docuri + '"');
}
} else {
if (docuri.indexOf('about:neterror') == 0) {
Components.utils.reportError('IN TAB - PROBLEM LOADING PAGE LOADED docuri = "' + docuri + '"');
}
}
}
window.gBrowser.addEventListener('DOMContentLoaded', listenToPageLoad_IfProblemLoadingPage, true);

How to make a cross domain http get request using javascript?

I'm trying to implement sms functionality in Dynamics CRM 2011. I've created a custom activity for this and added a button to the form of an SMS. When hitting the button, a sms should be send.
I need to make an http request for this and pass a few parameters. Here's the code triggered:
function send() {
var mygetrequest = new ajaxRequest()
mygetrequest.onreadystatechange = function () {
if (mygetrequest.readyState == 4) {
if (mygetrequest.status == 200 || window.location.href.indexOf("http") == -1) {
//document.getElementById("result").innerHTML = mygetrequest.responseText
alert(mygetrequest.responseText);
}
else {
alert("An error has occured making the request")
}
}
}
var nichandle = "MT-1234";
var hash = "md5";
var passphrase = "[encryptedpassphrase]";
var number = "32497123456";
var content = "testing sms service";
mygetrequest.open("GET", "http://api.smsaction.be/push/?nichandle=" + nichandle + "&hash=" + hash + "&passphrase=" + passphrase + "&number=" + number + "&content=" + content, true)
mygetrequest.send(null)
}
function ajaxRequest() {
var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] //activeX versions to check for in IE
if (window.ActiveXObject) { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken)
for (var i = 0; i < activexmodes.length; i++) {
try {
return new ActiveXObject(activexmodes[i])
}
catch (e) {
//suppress error
}
}
}
else if (window.XMLHttpRequest) // if Mozilla, Safari etc
return new XMLHttpRequest()
else
return false
}
I get the "access is denied error" on line:
mygetrequest.open("GET", "http://api.smsaction.be/push/?nichandle=" ......
Any help is appreciated.
The retrieving site has to approve cross domain AJAX requests. Usually, this is not the case.
You should contact smsaction.be or check their FAQ to see if they have any implementation in place.
Usually JSONP is used for cross domain requests, and this has to be implemented on both ends.
A good way to overcome this, is using your own site as a proxy. Do the AJAX requests to an script on your side, and let it do the call. In example PHP you can use cURL
I suppose the SMS-service is in different domain. If so, you cannot make AJAX-call to it, because it violates same origin policy. Basically you have two choices:
Do the SMS-sending on server-side
Use JSONP
Also, is it really so that the passphrase and other secrets are visible in HTML? What prevents people from stealing it and using it for their own purposes?
Your AJAX requests by default will fail because of Same Origin Policy.
http://en.wikipedia.org/wiki/Same_origin_policy
Modern techniques allow CORS ( see artilce by Nicholas ) http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
jQuery's Ajax allow CORS.
Another way to do it is to get the contents and dynamically generate a script element and do an insertBefore on head.firstchild ( refer jQuery 1.6.4 source line no : 7833 )
Google analytics code does some thing similar as well. you might want to take a look at that too.
Cheers..
Sree
For your example, when requesting from different domain error is:
XMLHttpRequest cannot load http://api.smsaction.be/push/?nichandle=??????&hash=?????&passphrase=[???????????]&number=????????????&content=???????????????. Origin http://server is not allowed by Access-Control-Allow-Origin.
For cross domains XMLHttp requests destination server must send Access-Control-Allow-Origin response header.
MDN: https://developer.mozilla.org/en/http_access_control

Getting URL of the top frame

Inside a facebook application I need to check what is the top frame (the main window) URL, and show content accordingly.
I tried using the following:
if (top.location.toString().toLowerCase().indexOf("facebook.com") <0) { ... }
Which works well if the page is not inside an iframe, but when the page is loaded within an iframe (as it does when used as facebook application) the code generates
"Uncaught TypeError: Property
'toString' of object # is not a
function".
Is there any way I can fix this code (with cross-browser compatibility - maybe with jQuery)?
Thanks!
Joel
It is true that cross origin concerns will prevent you from accessing this top window location. However, if you just want the parent window location of the iframe you can get at it via the document.referrer string.
Within your iframe you'd grab the url:
var parentURL = document.referrer
https://developer.mozilla.org/en-US/docs/Web/API/document.referrer
I've used this successfully in my own iframe apps. Also, be aware that if you navigate within your iframe the referrer will change.
Nicholas Zakas has a write-up on his blog:
http://www.nczonline.net/blog/2013/04/16/getting-the-url-of-an-iframes-parent/
The problem you are having that you are not allowed to access top.location across different document domains.
This is a security feature built in to browsers.
Read up on XSS and why the security precautions are in place :)
You can also learn a great deal by reading about the same origin policy
With Martin Jespersen adviced fix, I could check address in iFrame and standart top address:
//this is fix for IE
if (!window.location.origin) {
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
}
//and here we will get object of address
var urls = (window.location != window.parent.location) ? document.referrer: document.location;
//Martins adviced fix for checking if You are not in iFrame
if (window.top === window) {
urls = urls.origin;
}
//and now indexOf works in both ways - for iFrame and standart top address
if (urls.indexOf("facebook.com") != -1 ) {
//do stuff
}
This could work:
if (self!=top && document.referrer.toLowerCase().indexOf("facebook.com") <0) { ... }
...as long as you don't navigate inside the frame.
But it's not really a good solution ^^
If you need as much information as possible about the top page location:
function getTopLinkInfo() {
var topLinkInfo = {};
try {
// Only for same origins
topLinkInfo.topHref = top.location.href;
}
// Security exception: different origins
catch (error) {
try {
var ancestorOrigins = window.location.ancestorOrigins;
// Firefox doesn't support window.location.ancestorOrigins
if (ancestorOrigins) {
topLinkInfo.parentsDomains = [];
for (var i = 0; i < ancestorOrigins.length; i++) {
topLinkInfo.parentsDomains.unshift(ancestorOrigins[i]);
}
}
// Sometimes referrer refers to the parent URL (but not always,
// e.g. after iframe redirects).
var bottomW = window;
var topW = window.parent;
topLinkInfo.parentsReferrers = [];
// In case of empty referrers
topLinkInfo.parentsHrefs = [];
while (topW !== bottomW) {
topLinkInfo.parentsReferrers.unshift(bottomW.document.referrer);
topLinkInfo.parentsHrefs.unshift(bottomW.location.href);
bottomW = bottomW.parent;
topW = topW.parent;
}
} catch (error) {/* Permission denied to access a cross-origin frame */}
}
return topLinkInfo;
}
var result = getTopLinkInfo();
console.table(result);
console.info(result);

How can I determine if the document.referrer is from my own site?

Each time a page is requested I get the referrer of the page it came from. I need to track just referrer from other sites, I don't want to track going from one page to another within my site. How can I do that?
document.referrer.indexOf(location.protocol + "//" + location.host) === 0;
Originally posted at JavaScript - Am I the Referrer?
When someone comes to our website for the first time, we store the referrer in a cookie. This way, if they download our demo, we can get the original referrer from the cookie and we learn what sites are effective in driving leads to us.
Of course, every subsequent page a visitor hits on our website will show the referrer as our website. We don't want those. What we first did to avoid this was look for the text "windward" in the referrer and if so, assume that was from our site. The problem with this is we found a lot of referrer urls now have windward in them, either as a search term or part of a url that talks about Windward. (This is good news, it means we are now a well known product.)
So that brought me to our most recent approach. This should work for any site and should only reject referrers from the same site.
function IsReferredFromMe()
{
var ref = document.referrer;
if ((ref == null) || (ref.length == 0)) {
return false;
}
if (ref.indexOf("http://") == 0) {
ref = ref.substring(7);
}
ref = ref.toLowerCase();
var myDomain = document.domain;
if ((myDomain == null) || (myDomain.length == 0)) {
return false;
}
if (myDomain.indexOf("http://") == 0) {
myDomain = myDomain.substring(7);
}
myDomain = myDomain.toLowerCase();
return ref.indexOf(myDomain) == 0;
}
Solutions presented works in case of no sub domain in website in case of sub domain is there then we have to check just before the domain itself if any sub domains presented:
document.referrer.replace("http://", '').replace("https://", '').split('/')[0].match(new RegExp(".*" +location.host.replace("www.", '')))
this solution will add .* before the domain to detect that sub domain is from same domain.
If pages of “the same website” you think have the same origin (the same protocol, host, and port.),
check it this way:
function the_referrer_has_the_same_origin() {
try {
const referrer = new URL(document.referrer);
return (referrer.origin === location.origin);
} catch(invalid_url_error) {
return false;
}
}
// Works as intended for `https://www.google.com` and `https://www.google.com:443`.
.
If you’d like a short one and not to consider unlikely situations, try this:
document.referrer.startsWith(location.origin)
// Fails for `https://www.google.com` and `https://www.google.com:443`.
.
document.referrer.includes(location.host);

Categories

Resources