Mobile minibrowser: how to reload image while maintaing page offset position? - javascript

In mobile web minibrowsers, it seems that the window object makes no sense — since there's only one window to show. Therefore, things like window.pageYOffset=320 make no sense (as far as I can tell).
Given a simple example such as a map zoomin/zoomout such as
<html>
<head>
<title>zoom.html</title>
<script language="javascript">
var zoom=15;
var vpos=window.pageYOffset;
var key="whatever-your-Google-Maps-site-key-is";
function setImgSrc(z) {
document.getElementById('img').src=
"http://maps.google.com/maps/api/staticmap?center=Lisbon,Portugal&zoom="
+zoom+"&size=400x400&maptype=roadmap&sensor=false&key="+key;
}
function zoomin()
{ if(zoom<=18) zoom++; vpos=window.pageYOffset; setImgSrc(zoom); }
function zoomout()
{ if(zoom>=1) zoom--; vpos=window.pageYOffset; setImgSrc(zoom); }
</script>
</head>
<body onload="javascript:setImgSrc(15);">
<h1>zoom</h1>
<p><img id="img" alt="Lisbon,Portugal"/></p><p>
<a onclick="javascript:zoomin()">[+]</a>
<a onclick="javascript:zoomout()">[–</a>
</p><hr></body></html>
which seems to work well on desktop browsers; so I ask: how can I indicate that, on updating the page (onclick doesn't seem to work on minibrowsers, but href does) it should offset the page to the previous position?
(For other reasons, simply (re)loading the page to a given named anchor isn't working on the problem I'm dealing with.)
Thanks in advance for your feedback.

Isn't it lovely when you answer your own questions? Hmmm...
Anyway, according to this, some properties/functions for the JavaScript (JScript?) window object have different representations according to the choice of browser (Firefox, IE, etc.). Therefore, a possible page vertical offset function would be
function setPageVPos(w,vpos) {
try { w.scrollTo(0,vpos); } catch(e) {}
try { w.document.body.scrollTop(vpos); } catch(e) {}
}
The first tries with the window.scrollTo implementation in Firefox browsers, and the second tries with the document.body.scrollTop implementation in IE browsers. I've tested in both Firefox (desktop) browser and in a HP iPaq IE-like (mobile) minibrowser and works on both.

Related

focus back to parent object failing in latest version of chrome

background:
i have developed and maintain and a web based sales system using classic asp, javascript and sql server for internal use. to minimise my workload I restrict access to system so that only IE and Chrome browsers can be used.
problem
following latest chrome upgrade (Version 34.0.1847.116), routines that have been working perfectly are no longer working in chrome. IE is fine.
overview
i use parent to call child window. search for data in child, then return variables back to parent, which closes child, processes variables and updates parent form. I use this in lots of screens within the system and i find it provides a neat and intuitive UI.
However, from what I have read, Google Chrome has now restricted this functionality and there are no workarounds. It is such a useful function, I wanted to ask who uses pop up windows in this way and what solutions are there? Thanks so much in advance for your advice.
code
parent: calls new child window:
Add Customer
function upload(){
....
newWindow2 = window.open(newWindow2Ref, "custWin", features);
}
child: pop up to search and find customer and then passes back variable (custId) to opener giving field dummy1 the focus
opener.document.star.dummy1[<%=passedptr%>].value = "<%=custId%>";
opener.document.star.dummy1[<%=passedptr%>].focus();
parent: dummy 1 input gains focus and calls guestChanged function
<input name="dummy1" value="" type="readonly" onFocus="javascript: guestChanged(<%=nameCounter%>)" style="width:1px; height:1px; border:0">
function guestChanged(custPtr) {
if (newWindow2 && !newWindow2.closed) {
newWindow2.close();
counter = document.star.counter.value*1;
dummy1 = document.star.dummy1[0].value;
errorFlag = false;
...etc.
As I say, this is working perfectly in IE and until yesterday in Chrome :(
I hope I have explained the circumstances sufficiently well.
Thank you for taking the time to read this.
answered my own question with a shove in the right direction from #lankymart (thanks)
posted in case of interest to anyone running into same issue
parent:
<input name="passedGuestId" id="passedGuestId" onFocus="javascript: guestChanged(<%=nameCounter%>)">
<input type=button onclick="javascript:openwindow(0)" value="Add Customer">
<script language="javascript">
function openwindow(ptr) {
newWindow2Ref = "www.child.asp";
window.open(newWindow2Ref,"_blank","height=600,width=600,status=yes,toolbar=no,menubar=no,location=no")
}
</script>
child:
in my case, search on form in classic asp then run js, which closes window and returns value
<SCRIPT LANGUAGE="JavaScript">
<!--
window.opener.document.getElementById('passedGuestId').value="<%=custId%>"
window.opener.document.getElementById('passedGuestId').focus();
window.close()
// -->
</script>
parent
on return, input passedGuestId gets focus and calls guestChanged() Js to process the information returned
function guestChanged(custPtr) {
counter = document.star.counter.value*1;
dummy1 = document.star.passedGuestId.value;
errorFlag = false;
etc...

How to dynamically resize an iFrame (crossbrowser solution)

I am trying to include an iframe on a Drupal page. So far I successfully included the frame but the height resize function that I wrote only works in Internet Explorer. My goal is to make resize work inFirefox and Chrome as well. I searched the Internet but I couldn't find what I was looking for. Long story short, I want my frame to automatically resize height itself.
This is what I did so far (this is the code of the HTML page included in Drupal):
<script type="text/javascript">
function resize() {
var iframe = document.all.icw;
document.getElementById("icw").style.height = iframe.document.body.scrollHeight + "px";
}
</script>
<iframe id="icw" src="XXXXX" width="100%" scrolling="no" onload="resize()">
I understand that if the iframe lives on another domain the solution may be more difficult due to permissions. Is that correct?
These are the errors I get in Chrome and Firefox:
Chrome: Cannot read property 'body' of undefined
Chrome: JavaScript attempt to access frame with URL XXX from frame with URL YYY. Domains, protocols and ports must match.
Firefox: document.all is undefined
EDIT: What if I want to change one of the iframe element? In my case I need to change a textbox value. I understand I must do that when the page is loaded but I can't find a solution. No matter where I put the code I always a get a
Cannot call method 'getElementById' of undefined
when I try to access the iFrame textbox.
EDIT2: Asked a new question since they weren't related.
This solution works even cross-domain as long as you have access to both. There are a bunch of edge cases, but at least you can start with this. You can also add timer event to make sure iFrame is always a correct size so you never have scrollbar. Keep in mind this only works with browser supporting HTML5 due to the use of postMessage.
Inside iFrame
function resize(){
var body = document.body,
html = document.documentElement,
height = body.offsetHeight;
if(height === 0){
height = html.offsetHeight;
}
parent.postMessage({'action':'RESIZE', 'height':height}, '*');
}
Parent
function handleMessage(e) {
if (e.data.action == 'RESIZE') {
var targetHeight = e.data.height;
$('#iFrame').height(targetHeight);
}
}
window.addEventListener("message", handleMessage, false);
window.addEventListener("message", handleMessage, false);
This code do not work for IE.
For IE use
window.attachEvent("onmessage", handleMessage, false);
Check out this little library that does this and manages all the other edge cases for you.
https://github.com/davidjbradshaw/iframe-resizer

Checking Third-Party iFrame Content [duplicate]

Let's say you don't want other sites to "frame" your site in an <iframe>:
<iframe src="http://example.org"></iframe>
So you insert anti-framing, frame busting JavaScript into all your pages:
/* break us out of any containing iframes */
if (top != self) { top.location.replace(self.location.href); }
Excellent! Now you "bust" or break out of any containing iframe automatically. Except for one small problem.
As it turns out, your frame-busting code can be busted, as shown here:
<script type="text/javascript">
var prevent_bust = 0
window.onbeforeunload = function() { prevent_bust++ }
setInterval(function() {
if (prevent_bust > 0) {
prevent_bust -= 2
window.top.location = 'http://example.org/page-which-responds-with-204'
}
}, 1)
</script>
This code does the following:
increments a counter every time the browser attempts to navigate away from the current page, via the window.onbeforeunload event handler
sets up a timer that fires every millisecond via setInterval(), and if it sees the counter incremented, changes the current location to a server of the attacker's control
that server serves up a page with HTTP status code 204, which does not cause the browser to navigate anywhere
My question is -- and this is more of a JavaScript puzzle than an actual problem -- how can you defeat the frame-busting buster?
I had a few thoughts, but nothing worked in my testing:
attempting to clear the onbeforeunload event via onbeforeunload = null had no effect
adding an alert() stopped the process let the user know it was happening, but did not interfere with the code in any way; clicking OK lets the busting continue as normal
I can't think of any way to clear the setInterval() timer
I'm not much of a JavaScript programmer, so here's my challenge to you: hey buster, can you bust the frame-busting buster?
FWIW, most current browsers support the X-Frame-Options: deny directive, which works even when script is disabled.
IE8:
http://blogs.msdn.com/ie/archive/2009/01/27/ie8-security-part-vii-clickjacking-defenses.aspx
Firefox (3.6.9)
https://bugzilla.mozilla.org/show_bug.cgi?id=475530
https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header
Chrome/Webkit
http://blog.chromium.org/2010/01/security-in-depth-new-security-features.html
http://trac.webkit.org/changeset/42333
I'm not sure if this is viable or not - but if you can't break the frame, why not just display a warning. For example, If your page isn't the "top page" create a setInterval method that tries to break the frame. If after 3 or 4 tries your page still isn't the top page - create a div element that covers the whole page (modal box) with a message and a link like...
You are viewing this page in a unauthorized frame window - (Blah blah... potential security issue)
click this link to fix this problem
Not the best, but I don't see any way they could script their way out of that.
We have used the following approach in one of our websites from http://seclab.stanford.edu/websec/framebusting/framebust.pdf
<style>
body {
display : none
}
</style>
<script>
if(self == top) {
document.getElementsByTagName("body")[0].style.display = 'block';
}
else{
top.location = self.location;
}
</script>
Came up with this, and it seems to work at least in Firefox and the Opera browser.
if(top != self) {
top.onbeforeunload = function() {};
top.location.replace(self.location.href);
}
Considering current HTML5 standard that introduced sandbox for iframe, all frame busting codes that provided in this page can be disabled when attacker uses sandbox because it restricts the iframe from following:
allow-forms: Allow form submissions.
allow-popups: Allow opening popup windows.
allow-pointer-lock: Allow access to pointer movement and pointer lock.
allow-same-origin: Allow access to DOM objects when the iframe loaded form same origin
allow-scripts: Allow executing scripts inside iframe
allow-top-navigation: Allow navigation to top level window
Please see: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox
Now, consider attacker used the following code to host your site in iframe:
<iframe src="URI" sandbox></iframe>
Then, all JavaScript frame busting code will fail.
After checking all frame busing code, only this defense works in all cases:
<style id="antiClickjack">body{display:none !important;}</style>
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
that originally proposed by Gustav Rydstedt, Elie Bursztein, Dan Boneh, and Collin Jackson (2010)
After pondering this for a little while, I believe this will show them who's boss...
if(top != self) {
window.open(location.href, '_top');
}
Using _top as the target parameter for window.open() will launch it in the same window.
As of 2015, you should use CSP2's frame-ancestors directive for this. This is implemented via an HTTP response header.
e.g.
Content-Security-Policy: frame-ancestors 'none'
Of course, not many browsers support CSP2 yet so it is wise to include the old X-Frame-Options header:
X-Frame-Options: DENY
I would advise to include both anyway, otherwise your site would continue to be vulnerable to Clickjacking attacks in old browsers, and of course you would get undesirable framing even without malicious intent. Most browsers do update automatically these days, however you still tend to get corporate users being stuck on old versions of Internet Explorer for legacy application compatibility reasons.
All the proposed solutions directly force a change in the location of the top window. What if a user wants the frame to be there? For example the top frame in the image results of search engines.
I wrote a prototype where by default all inputs (links, forms and input elements) are disabled and/or do nothing when activated.
If a containing frame is detected, the inputs are left disabled and a warning message is shown at the top of the page. The warning message contains a link that will open a safe version of the page in a new window. This prevents the page from being used for clickjacking, while still allowing the user to view the contents in other situations.
If no containing frame is detected, the inputs are enabled.
Here is the code. You need to set the standard HTML attributes to safe values and add additonal attributes that contain the actual values. It probably is incomplete and for full safety additional attributes (I am thinking about event handlers) will probably have to be treated in the same way:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title></title>
<script><!--
function replaceAttributeValuesWithActualOnes( array, attributeName, actualValueAttributeName, additionalProcessor ) {
for ( var elementIndex = 0; elementIndex < array.length; elementIndex += 1 ) {
var element = array[ elementIndex ];
var actualValue = element.getAttribute( actualValueAttributeName );
if ( actualValue != null ) {
element[ attributeName ] = actualValue;
}
if ( additionalProcessor != null ) {
additionalProcessor( element );
}
}
}
function detectFraming() {
if ( top != self ) {
document.getElementById( "framingWarning" ).style.display = "block";
} else {
replaceAttributeValuesWithActualOnes( document.links, "href", "acme:href" );
replaceAttributeValuesWithActualOnes( document.forms, "action", "acme:action", function ( form ) {
replaceAttributeValuesWithActualOnes( form.elements, "disabled", "acme:disabled" );
});
}
}
// -->
</script>
</head>
<body onload="detectFraming()">
<div id="framingWarning" style="display: none; border-style: solid; border-width: 4px; border-color: #F00; padding: 6px; background-color: #FFF; color: #F00;">
<div>
<b>SECURITY WARNING</b>: Acme App is displayed inside another page.
To make sure your data is safe this page has been disabled.<br>
Continue working safely in a new tab/window
</div>
</div>
<p>
Content. Do something
</p>
<form name="acmeForm" action="#" acme:action="real-action.html">
<p>Name: <input type="text" name="name" value="" disabled="disabled" acme:disabled=""></p>
<p><input type="submit" name="save" value="Save" disabled="disabled" acme:disabled=""></p>
</form>
</body>
</html>
if (top != self) {
top.location.replace(location);
location.replace("about:blank"); // want me framed? no way!
}
I'm going to be brave and throw my hat into the ring on this one (ancient as it is), see how many downvotes I can collect.
Here is my attempt, which does seem to work everywhere I have tested it (Chrome20, IE8 and FF14):
(function() {
if (top == self) {
return;
}
setInterval(function() {
top.location.replace(document.location);
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.open(
'get',
'http://mysite.tld/page-that-takes-a-while-to-load',
false
);
xhr.send(null);
}, 0);
}, 1);
}());
I placed this code in the <head> and called it from the end of the <body> to ensure my page is rendered before it starts arguing with the malicious code, don't know if this is the best approach, YMMV.
How does it work?
...I hear you ask - well the honest answer is, I don't really know. It took a lot of fudging about to make it work everywhere I was testing, and the exact effect that it has varies slightly depending on where you run it.
Here is the thinking behind it:
Set a function to run at the lowest possible interval. The basic concept behind any of the realistic solutions I have seen is to fill up the scheduler with more events than the frame buster-buster has.
Every time the function fires, try and change the location of the top frame. Fairly obvious requirement.
Also schedule a function to run immediately which will take a long time to complete (thereby blocking the frame buster-buster from interfering with the location change). I chose a synchronous XMLHttpRequest because it's the only mechanism I can think of that doesn't require (or at least ask for) user interaction and doesn't chew up the user's CPU time.
For my http://mysite.tld/page-that-takes-a-while-to-load (the target of the XHR) I used a PHP script that looks like this:
<?php sleep(5);
What happens?
Chrome and Firefox wait the 5 seconds while the XHR completes, then successfully redirect to the framed page's URL.
IE redirects pretty much immediately
Can't you avoid the wait time in Chrome and Firefox?
Apparently not. At first I pointed the XHR to a URL that would return a 404 - this didn't work in Firefox. Then I tried the sleep(5); approach that I eventually landed on for this answer, then I started playing around with the sleep length in various ways. I could find no real pattern to the behaviour, but I did find that if it is too short, specifically Firefox will not play ball (Chrome and IE seem to be fairly well behaved). I don't know what the definition of "too short" is in real terms, but 5 seconds seems to work every time.
If any passing Javascript ninjas want to explain a little better what's going on, why this is (probably) wrong, unreliable, the worst code they've ever seen etc I'll happily listen.
Ok, so we know that were in a frame. So we location.href to another special page with the path as a GET variable. We now explain to the user what is going on and provide a link with a target="_TOP" option. It's simple and would probably work (haven't tested it), but it requires some user interaction. Maybe you could point out the offending site to the user and make a hall of shame of click jackers to your site somewhere.. Just an idea, but it night work..
Well, you can modify the value of the counter, but that is obviously a brittle solution. You can load your content via AJAX after you have determined the site is not within a frame - also not a great solution, but it hopefully avoids firing the on beforeunload event (I am assuming).
Edit: Another idea. If you detect you are in a frame, ask the user to disable javascript, before clicking on a link that takes you to the desired URL (passing a querystring that lets your page know to tell the user that they can re-enable javascript once they are there).
Edit 2: Go nuclear - if you detect you are in a frame, just delete your document body content and print some nasty message.
Edit 3: Can you enumerate the top document and set all functions to null (even anonymous ones)?
If you add an alert right after the buster code, then the alert will stall the javascript thread, and it will let the page load. This is what StackOverflow does, and it busts out of my iframes, even when I use the frame busting buster. It also worked with my simple test page. This has only been tested in Firefox 3.5 and IE7 on windows.
Code:
<script type="text/javascript">
if (top != self){
top.location.replace(self.location.href);
alert("for security reasons bla bla bla");
}
</script>
I think you were almost there. Have you tried:
window.parent.onbeforeunload = null;
window.parent.location.replace(self.location.href);
or, alternatively:
window.parent.prevent_bust = 0;
Note: I didn't actually test this.
If you look at the values returned by setInterval() they are usually single digits, so you can usually disable all such interrupts with a single line of code:
for (var j = 0 ; j < 256 ; ++j) clearInterval(j)
What about calling the buster repeatedly as well? This'll create a race condition, but one may hope that the buster comes out on top:
(function() {
if(top !== self) {
top.location.href = self.location.href;
setTimeout(arguments.callee, 0);
}
})();
I might just have just gotten a way to bust the frame buster buster javascript. Using the getElementsByName in my javascript function, i've set a loop between the frame buster and the actual frame buster buster script.
check this post out. http://www.phcityonweb.com/frame-buster-buster-buster-2426
setInterval and setTimeout create an automatically incrementing interval. Each time setTimeout or setInterval is called, this number goes up by one, so that if you call setTimeout, you'll get the current, highest value.
var currentInterval = 10000;
currentInterval += setTimeout( gotoHREF, 100 );
for( var i = 0; i < currentInterval; i++ ) top.clearInterval( i );
// Include setTimeout to avoid recursive functions.
for( i = 0; i < currentInterval; i++ ) top.clearTimeout( i );
function gotoHREF(){
top.location.href = "http://your.url.here";
}
Since it is almost unheard of for there to be 10000 simultaneous setIntervals and setTimeouts working, and since setTimeout returns "last interval or timeout created + 1", and since top.clearInterval is still accessible, this will defeat the black-hat attacks to frame websites which are described above.
Use htaccess to avoid high-jacking frameset, iframe and any content like images.
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^http://www\.yoursite\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteRule ^(.*)$ /copyrights.html [L]
This will show a copyright page instead of the expected.
You could improve the whole idea by using the postMessage() method to allow some domains to access and display your content while blocking all the others. First, the container-parent must introduce itself by posting a message to the contentWindow of the iframe that is trying to display your page. And your page must be ready to accept messages,
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
// Use event.origin here like
if(event.origin == "https://perhapsyoucantrustthisdomain.com"){
// code here to block/unblock access ... a method like the one in user1646111's post can be good.
}
else{
// code here to block/unblock access ... a method like the one in user1646111's post can be good.
}
}
Finally don't forget to wrap things inside functions that will wait for load events.

Print section of page script works perfectly in IE but problems in Firefox

In my battles to find a solution to printing just one area of the page that works within WordPress, I came across an excellent little script that meets my needs perfectly.. but only in IE browser. For some reason Firefox doesn't want to play ball.
The script is:
function printURL(sHref) {
if(document.getElementById && document.all && sHref) {
if(!self.oPrintElm) {
var aHeads = document.getElementsByTagName('HEAD');
if(!aHeads || !aHeads.length)
return false;
if(!self.oPrintElm)
self.oPrintElm = document.createElement('LINK');
self.oPrintElm.rel = 'alternate';
self.oPrintElm.media = 'print';
aHeads[0].appendChild(self.oPrintElm);
}
self.oPrintElm.href = sHref;
self.focus();
self.print();
return true;
}
else
return false;
}
Called by:
<a onclick="printURL(this.href); return false;" href="http://printstuff.com" target="_blank">print</a>
This is working in IE, but not FF. I don't know much about JavaScript, so would appreciate if you could tell me if there's anything you see that's giving Firefox headaches.
By the way - I have to go a javascript route instead of using a print CSS file, as the area I want to print (a coupon) is set in a table which is obviously set in the WordPress theme's container and wrapper divs which makes it difficult to isolate it for printing.
I've also experimented with iframe printing, which I made some headway with, but IE gives me problems there (rolleyes). So this script above seems a good answer to me, except Firefox does nothing when I click 'print'. Thanks a lot.
document.all tests false in all browsers other than IE. So your code is very explicitly only running the self.print() line in IE only.

Frame Buster Buster ... buster code needed

Let's say you don't want other sites to "frame" your site in an <iframe>:
<iframe src="http://example.org"></iframe>
So you insert anti-framing, frame busting JavaScript into all your pages:
/* break us out of any containing iframes */
if (top != self) { top.location.replace(self.location.href); }
Excellent! Now you "bust" or break out of any containing iframe automatically. Except for one small problem.
As it turns out, your frame-busting code can be busted, as shown here:
<script type="text/javascript">
var prevent_bust = 0
window.onbeforeunload = function() { prevent_bust++ }
setInterval(function() {
if (prevent_bust > 0) {
prevent_bust -= 2
window.top.location = 'http://example.org/page-which-responds-with-204'
}
}, 1)
</script>
This code does the following:
increments a counter every time the browser attempts to navigate away from the current page, via the window.onbeforeunload event handler
sets up a timer that fires every millisecond via setInterval(), and if it sees the counter incremented, changes the current location to a server of the attacker's control
that server serves up a page with HTTP status code 204, which does not cause the browser to navigate anywhere
My question is -- and this is more of a JavaScript puzzle than an actual problem -- how can you defeat the frame-busting buster?
I had a few thoughts, but nothing worked in my testing:
attempting to clear the onbeforeunload event via onbeforeunload = null had no effect
adding an alert() stopped the process let the user know it was happening, but did not interfere with the code in any way; clicking OK lets the busting continue as normal
I can't think of any way to clear the setInterval() timer
I'm not much of a JavaScript programmer, so here's my challenge to you: hey buster, can you bust the frame-busting buster?
FWIW, most current browsers support the X-Frame-Options: deny directive, which works even when script is disabled.
IE8:
http://blogs.msdn.com/ie/archive/2009/01/27/ie8-security-part-vii-clickjacking-defenses.aspx
Firefox (3.6.9)
https://bugzilla.mozilla.org/show_bug.cgi?id=475530
https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header
Chrome/Webkit
http://blog.chromium.org/2010/01/security-in-depth-new-security-features.html
http://trac.webkit.org/changeset/42333
I'm not sure if this is viable or not - but if you can't break the frame, why not just display a warning. For example, If your page isn't the "top page" create a setInterval method that tries to break the frame. If after 3 or 4 tries your page still isn't the top page - create a div element that covers the whole page (modal box) with a message and a link like...
You are viewing this page in a unauthorized frame window - (Blah blah... potential security issue)
click this link to fix this problem
Not the best, but I don't see any way they could script their way out of that.
We have used the following approach in one of our websites from http://seclab.stanford.edu/websec/framebusting/framebust.pdf
<style>
body {
display : none
}
</style>
<script>
if(self == top) {
document.getElementsByTagName("body")[0].style.display = 'block';
}
else{
top.location = self.location;
}
</script>
Came up with this, and it seems to work at least in Firefox and the Opera browser.
if(top != self) {
top.onbeforeunload = function() {};
top.location.replace(self.location.href);
}
Considering current HTML5 standard that introduced sandbox for iframe, all frame busting codes that provided in this page can be disabled when attacker uses sandbox because it restricts the iframe from following:
allow-forms: Allow form submissions.
allow-popups: Allow opening popup windows.
allow-pointer-lock: Allow access to pointer movement and pointer lock.
allow-same-origin: Allow access to DOM objects when the iframe loaded form same origin
allow-scripts: Allow executing scripts inside iframe
allow-top-navigation: Allow navigation to top level window
Please see: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox
Now, consider attacker used the following code to host your site in iframe:
<iframe src="URI" sandbox></iframe>
Then, all JavaScript frame busting code will fail.
After checking all frame busing code, only this defense works in all cases:
<style id="antiClickjack">body{display:none !important;}</style>
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
that originally proposed by Gustav Rydstedt, Elie Bursztein, Dan Boneh, and Collin Jackson (2010)
After pondering this for a little while, I believe this will show them who's boss...
if(top != self) {
window.open(location.href, '_top');
}
Using _top as the target parameter for window.open() will launch it in the same window.
As of 2015, you should use CSP2's frame-ancestors directive for this. This is implemented via an HTTP response header.
e.g.
Content-Security-Policy: frame-ancestors 'none'
Of course, not many browsers support CSP2 yet so it is wise to include the old X-Frame-Options header:
X-Frame-Options: DENY
I would advise to include both anyway, otherwise your site would continue to be vulnerable to Clickjacking attacks in old browsers, and of course you would get undesirable framing even without malicious intent. Most browsers do update automatically these days, however you still tend to get corporate users being stuck on old versions of Internet Explorer for legacy application compatibility reasons.
All the proposed solutions directly force a change in the location of the top window. What if a user wants the frame to be there? For example the top frame in the image results of search engines.
I wrote a prototype where by default all inputs (links, forms and input elements) are disabled and/or do nothing when activated.
If a containing frame is detected, the inputs are left disabled and a warning message is shown at the top of the page. The warning message contains a link that will open a safe version of the page in a new window. This prevents the page from being used for clickjacking, while still allowing the user to view the contents in other situations.
If no containing frame is detected, the inputs are enabled.
Here is the code. You need to set the standard HTML attributes to safe values and add additonal attributes that contain the actual values. It probably is incomplete and for full safety additional attributes (I am thinking about event handlers) will probably have to be treated in the same way:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title></title>
<script><!--
function replaceAttributeValuesWithActualOnes( array, attributeName, actualValueAttributeName, additionalProcessor ) {
for ( var elementIndex = 0; elementIndex < array.length; elementIndex += 1 ) {
var element = array[ elementIndex ];
var actualValue = element.getAttribute( actualValueAttributeName );
if ( actualValue != null ) {
element[ attributeName ] = actualValue;
}
if ( additionalProcessor != null ) {
additionalProcessor( element );
}
}
}
function detectFraming() {
if ( top != self ) {
document.getElementById( "framingWarning" ).style.display = "block";
} else {
replaceAttributeValuesWithActualOnes( document.links, "href", "acme:href" );
replaceAttributeValuesWithActualOnes( document.forms, "action", "acme:action", function ( form ) {
replaceAttributeValuesWithActualOnes( form.elements, "disabled", "acme:disabled" );
});
}
}
// -->
</script>
</head>
<body onload="detectFraming()">
<div id="framingWarning" style="display: none; border-style: solid; border-width: 4px; border-color: #F00; padding: 6px; background-color: #FFF; color: #F00;">
<div>
<b>SECURITY WARNING</b>: Acme App is displayed inside another page.
To make sure your data is safe this page has been disabled.<br>
Continue working safely in a new tab/window
</div>
</div>
<p>
Content. Do something
</p>
<form name="acmeForm" action="#" acme:action="real-action.html">
<p>Name: <input type="text" name="name" value="" disabled="disabled" acme:disabled=""></p>
<p><input type="submit" name="save" value="Save" disabled="disabled" acme:disabled=""></p>
</form>
</body>
</html>
if (top != self) {
top.location.replace(location);
location.replace("about:blank"); // want me framed? no way!
}
I'm going to be brave and throw my hat into the ring on this one (ancient as it is), see how many downvotes I can collect.
Here is my attempt, which does seem to work everywhere I have tested it (Chrome20, IE8 and FF14):
(function() {
if (top == self) {
return;
}
setInterval(function() {
top.location.replace(document.location);
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.open(
'get',
'http://mysite.tld/page-that-takes-a-while-to-load',
false
);
xhr.send(null);
}, 0);
}, 1);
}());
I placed this code in the <head> and called it from the end of the <body> to ensure my page is rendered before it starts arguing with the malicious code, don't know if this is the best approach, YMMV.
How does it work?
...I hear you ask - well the honest answer is, I don't really know. It took a lot of fudging about to make it work everywhere I was testing, and the exact effect that it has varies slightly depending on where you run it.
Here is the thinking behind it:
Set a function to run at the lowest possible interval. The basic concept behind any of the realistic solutions I have seen is to fill up the scheduler with more events than the frame buster-buster has.
Every time the function fires, try and change the location of the top frame. Fairly obvious requirement.
Also schedule a function to run immediately which will take a long time to complete (thereby blocking the frame buster-buster from interfering with the location change). I chose a synchronous XMLHttpRequest because it's the only mechanism I can think of that doesn't require (or at least ask for) user interaction and doesn't chew up the user's CPU time.
For my http://mysite.tld/page-that-takes-a-while-to-load (the target of the XHR) I used a PHP script that looks like this:
<?php sleep(5);
What happens?
Chrome and Firefox wait the 5 seconds while the XHR completes, then successfully redirect to the framed page's URL.
IE redirects pretty much immediately
Can't you avoid the wait time in Chrome and Firefox?
Apparently not. At first I pointed the XHR to a URL that would return a 404 - this didn't work in Firefox. Then I tried the sleep(5); approach that I eventually landed on for this answer, then I started playing around with the sleep length in various ways. I could find no real pattern to the behaviour, but I did find that if it is too short, specifically Firefox will not play ball (Chrome and IE seem to be fairly well behaved). I don't know what the definition of "too short" is in real terms, but 5 seconds seems to work every time.
If any passing Javascript ninjas want to explain a little better what's going on, why this is (probably) wrong, unreliable, the worst code they've ever seen etc I'll happily listen.
Ok, so we know that were in a frame. So we location.href to another special page with the path as a GET variable. We now explain to the user what is going on and provide a link with a target="_TOP" option. It's simple and would probably work (haven't tested it), but it requires some user interaction. Maybe you could point out the offending site to the user and make a hall of shame of click jackers to your site somewhere.. Just an idea, but it night work..
Well, you can modify the value of the counter, but that is obviously a brittle solution. You can load your content via AJAX after you have determined the site is not within a frame - also not a great solution, but it hopefully avoids firing the on beforeunload event (I am assuming).
Edit: Another idea. If you detect you are in a frame, ask the user to disable javascript, before clicking on a link that takes you to the desired URL (passing a querystring that lets your page know to tell the user that they can re-enable javascript once they are there).
Edit 2: Go nuclear - if you detect you are in a frame, just delete your document body content and print some nasty message.
Edit 3: Can you enumerate the top document and set all functions to null (even anonymous ones)?
If you add an alert right after the buster code, then the alert will stall the javascript thread, and it will let the page load. This is what StackOverflow does, and it busts out of my iframes, even when I use the frame busting buster. It also worked with my simple test page. This has only been tested in Firefox 3.5 and IE7 on windows.
Code:
<script type="text/javascript">
if (top != self){
top.location.replace(self.location.href);
alert("for security reasons bla bla bla");
}
</script>
I think you were almost there. Have you tried:
window.parent.onbeforeunload = null;
window.parent.location.replace(self.location.href);
or, alternatively:
window.parent.prevent_bust = 0;
Note: I didn't actually test this.
If you look at the values returned by setInterval() they are usually single digits, so you can usually disable all such interrupts with a single line of code:
for (var j = 0 ; j < 256 ; ++j) clearInterval(j)
What about calling the buster repeatedly as well? This'll create a race condition, but one may hope that the buster comes out on top:
(function() {
if(top !== self) {
top.location.href = self.location.href;
setTimeout(arguments.callee, 0);
}
})();
I might just have just gotten a way to bust the frame buster buster javascript. Using the getElementsByName in my javascript function, i've set a loop between the frame buster and the actual frame buster buster script.
check this post out. http://www.phcityonweb.com/frame-buster-buster-buster-2426
setInterval and setTimeout create an automatically incrementing interval. Each time setTimeout or setInterval is called, this number goes up by one, so that if you call setTimeout, you'll get the current, highest value.
var currentInterval = 10000;
currentInterval += setTimeout( gotoHREF, 100 );
for( var i = 0; i < currentInterval; i++ ) top.clearInterval( i );
// Include setTimeout to avoid recursive functions.
for( i = 0; i < currentInterval; i++ ) top.clearTimeout( i );
function gotoHREF(){
top.location.href = "http://your.url.here";
}
Since it is almost unheard of for there to be 10000 simultaneous setIntervals and setTimeouts working, and since setTimeout returns "last interval or timeout created + 1", and since top.clearInterval is still accessible, this will defeat the black-hat attacks to frame websites which are described above.
Use htaccess to avoid high-jacking frameset, iframe and any content like images.
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^http://www\.yoursite\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteRule ^(.*)$ /copyrights.html [L]
This will show a copyright page instead of the expected.
You could improve the whole idea by using the postMessage() method to allow some domains to access and display your content while blocking all the others. First, the container-parent must introduce itself by posting a message to the contentWindow of the iframe that is trying to display your page. And your page must be ready to accept messages,
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
// Use event.origin here like
if(event.origin == "https://perhapsyoucantrustthisdomain.com"){
// code here to block/unblock access ... a method like the one in user1646111's post can be good.
}
else{
// code here to block/unblock access ... a method like the one in user1646111's post can be good.
}
}
Finally don't forget to wrap things inside functions that will wait for load events.

Categories