Frame Busting

  • This is a Version 2 script.
  • This script does not work in Opera 5.02+.
  • The version of the script I like best does not work in Netscape 6. Below I give ample notes.
  • The versions of this script that for security reasons should not work in any browser do work in Netscape 2 and Opera 4.
  •  

    This page explains the difficulties with frame busting: that is, preventing that your page is caught in someone else's frameset.

    This is a very difficult case since it touches on security: browsers sometimes don't allow you to view crucial information because this information comes from another server.

    The actual script has two parts: first of all finding out if something is wrong (= if your site is in someone else's frameset), secondly setting the location of the top frame to your site. So it should go something like:

    if (someone else's frameset)
    {
    	top frame location = your frameset/homepage
    }
    

    If you're in someone else's frameset, refresh the top frame to your frameset or homepage.

    Unfortunately both steps have their own problems. Most browsers offer a way of doing what you want without compromising security, but Netscape 6 and Opera 5.02+ are more difficult to handle. A reader found a solution to the Netscape 6 problem: it is possible to write a frame busting script for Netscape 6, but unfortunately the best version doesn't work.

    Security

    There are two security issues:

    1. Can you read out information about the top frame if the page in it does not come from your server?
    2. Can you change the location of the top frame if the page in it does not come from your server?

    Put simply, most browsers answer Depends to the first question and Yes to the second one, while Opera 5.02+ says No to both questions. In addition, Netscape 6 only allows one way of changing the location of the top frame, as far as I know.

    Unfortunately, in Explorer 5.5 and up on Windows, it is possible to read out interesting stuff from and even execute JavaScripts in a frame coming from another server. This bad bug is a gaping security hole, not least because the scripting is so simple.

    Reading out information

    The first security issue is the reading out of information about pages that come from another server than yours.

    Nearly all browsers prohibit the reading out of the location.href of a page in another frame or window that comes from another server than yours. This has a simple reason: it prevents snooping into the surfing behaviour of a user. If it wasn't prohibited, you could write an easy script that informs you of the pages loaded in the other open windows of your user's browser and sell this information to advertisers. No go.

    So this script

    if (top.location.href != self.location.href)
    	top.location.href = self.location.href;
    

    doesn't work. If the top frame comes from another server you may not read out top.location.href and the browser gives an error message ('Access denied' for instance).

    However, reading out other information is allowed. The most important thing is that you are allowed to see if a top frame is present and if your own frame is a child of this frame. This is what we're going to use later on.

    Changing top.location

    The second problem is that Netscape 6 does not allow you to change top.location.href if the page currently in the top is from another server. Even worse, it considers another port on the same server to be another server entirely. To my mind, this goes too far.

    At first I found a workaround: use a FORM with ACTION=thepage.html and submit it to the top frame. This works fine.

    <FORM METHOD=GET ACTION="thepage.html" TARGET="_top">
    

    The mystery was solved by an alert reader who did some testing of his own and found out that, while top.location.href cannot be changed in Netscape 6, top.location can! (Why? I have no idea.)
    Therefore, to make the script work in Netscape 6 we have to change top.location and bust frames in this way.

    The script

    There are many possibilities for writing a frame busting script. As said above, the variations that require you to read out top.location.href don't work, we need to do some more circumspect detecting.

    Detecting if something is wrong

    For this site I chose the detect:

    if ((top != self.parent) || (top == self))
    

    This check is specific for my own frameset. I include it in each content page of this site. Each content page should be in a frame directly below the top. So two things can be wrong.

    1. My top frame can be caught in someone else's frameset. This is checked by
      if ((top != self.parent)
      
      If the parent of this content frame is not the top frame, I'm framed and I should break out to my index page.
    2. The page itself can be in the top frame. Some search engines list the individual pages of my site, so a user can enter it at a content page. In that case I want to customize the frameset so they see my frames with the content page they want. This also means jumping to my index page. This is checked by the second part:
      || (top == self))
      
      If this page is the top frame, we should also go to the index page.

    You should use a variation of this script if your site contains frames. Please note that checking if specific frames exist may not be possible. I used to check if the frame named navi is directly below the top frame (it should be) by

    if (!top.navi)
    	// bust frames
    

    Unfortunately reading out these kinds of details about the frameset is not allowed for security reasons in Netscape 6 and Opera 5.02+, so you cannot use it.

    If you don't use frames, simply do:

    if (top != self)
    

    Your page should be in the top frame, if it isn't something is wrong and you should bust frames.

    Doing something about it

    Now that we have detected that something is wrong, we should do something about it. What should happen is that the top frame is refreshed to your page. Easiest would be:

    if ((top != self.parent) || (top == self))
    {
    	top.location = 'correct_page.html';
    }
    

    This solution works even in Netscape 6. However, there's one serious flaw. If a user is busted out to my index page and then presses the 'Back' button he's returned to the framed version of my site, so the frame busting kicks in and sends him back to my index page. This is not very nice.

    Therefore I use the replace() method. This method refreshes the page and overwrites the old page in the browser history. If the user presses 'Back' on my index page, he's sent back to the page he was on before entering my site. This is the only proper way of doing it. So I do

    if ((top != self.parent) || (top == self))
    {
    	top.location.replace('index.html?' + naar);
    }
    

    Unfortunately using the replace method is not allowed in Netscape 6 when dealing with a page that comes from another server, you can only use

    top.location = 'correct_page.html';
    

    So I had to choose between a user-unfriendly script that works in Netscape 6 and a user-friendly script that doesn't work in Netscape 6. I chose the last one, the not-being-able-to-use-Back-button effect is really too annoying to tolerate.

    Therefore my frame busting script still doesn't work in Netscape 6, even though it's technically possible.

    Home