Clickjacking

When I first heard about clickjacking, I was amazed at how easy it is to use this type of attack and what damage it can do. Later I was amazed at how easy it is to secure your site against clickjacking. Now I’m just amazed at how many websites are still vulnerable. I’ve been thinking about it for some time and the only reason I can come up with is a lack of awareness, so here’s my contribution to making this world a little better (safer).

First some background: In a clickjacking attack, a hacker attempts to ‘hijack’ clicks and send them to a different component then where the user expects them to go, causing all kind of actions on behalf of the unknowing user. This may be sending emails, creating users, transferring money, allowing unrestricted access to your webcam… or worse. Basically any action can be executed unknowlingly if a site is not properly protected. While clickjacking attempts to hijack clicks, it is also possible to hijack other mouse events, or key strokes. This is all part of something we call “window UI redress”. In a redress attack, a hacker will ‘redress’ a site, so it looks different from what the user is accustomed to. The user won’t probably even know that he has loaded that site. Usually the hacker does this by loading the site in an iframe and making that iframe completely invisible, while showing something different behind it. It is also possible to show something on top of the site, and passing all events on to the element behind it (in this case an element inside the iframe).

400px-Clickjacking_description(image borrowed from OWASP)

Let’s illustrate this with a simple example page a hacker can make:

<html>
  <body>  
    <iframe src="vulnerable-site.com/dangerous/function/" style="opacity: 0;" height="384" width="584" top="-123" left="-95" scrolling="no"></iframe>
    <button>Click for free iPad</button>
  </body>
</html>

The vulnerable site is loaded in an iframe, with opacity set to 0, making it invisible. It is positioned in such a way, that the location where the hacker wants a user to click is right behind some shiny button.

A nice real-world example that I like, is the clickjacking vulnerability in the Adobe Flash settings page in 2011 that allowed a hacker to view webcams from any user around the world without their knowledge: https://youtu.be/K8yrSUGa4go. Feross Aboukhadijeh explains how he found the bug and created a simple game to demonstrate it. This also shows that clickjacking does not just have to be about a single click, but can also mean complex patterns of clicks or other interactions.

Prevention

So, how do you prevent all this? The basic idea is to control whether your site can be loaded in an <iframe> (or <frame>, <object>, <embed> or <applet>) or not, and if it’s allowed, on what domains then.

Never load in an iframe

If it is never allowed to let your site be loaded in an iframe, prevention is easiest. There are two things you need to do: 1. add some HTTP headers and 2. add JavaScript protection.

First the HTTP headers. Add these to any response to the browser. Oh, and don’t forget that your mobile site needs them too! And also remember any .swf files you’re using, or other objects that can be included through their own request URL.

X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

The first header is old and not very flexible, but it’s the only thing that works in most versions of Internet Explorer and some other browsers. The second header is newer and therefore unfortunately not yet (fully) supported in all browsers. So you will want to use both headers.

Second, you should also add a frame-busting script that checks if your site is the top-level domain and redirects the user to safety if it isn’t.

<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>

There are many different frame-busting scripts, but according to the OWASP anti-clickjacking cheat sheet, this is currently the ‘best-for-now’ version. It will first ‘hide’ the whole site with a simple styling rule, and then, if your site appears not to be in an iframe, it will remove that rule again. Hackers have many ways of loading your page in an iframe and breaking the JavaScript, but because of the styling rule no harm can be done if they try. To give you an idea why JavaScript alone is not enough: hackers could run the site in a sandboxed iframe and disable all JavaScript, or they can use the browsers anti-XSS protection to disable the frame-busting script, or (in older browsers) they can redefine the meaning of the JavaScript variable ‘location’ and thereby changing the working of the script.

If you put the above styling and script in the head-section of your page, you should be safe. Well, … as safe as can be expected on the Internet. And again, don’t forget to include the script also in your mobile site.

only load in iframes on a specific domain

Sometimes it is required that your site can run in an iframe. Often this is only necessary on sites that are on the same domain. For example, because your web application runs in a web portal that is created somewhere else in your organization. In this case there is no JavaScript solution possible, because the same-origin policy forbids us to see the domain of the parent page, so there is no way to check if that page is allowed to include your site. Fortunately the HTTP headers have options for this situation.

X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'

If your site needs to run in an iframe on a specific domain, simply specify the domain:

X-Frame-Options: ALLOW-FROM https://different-domain.com
Content-Security-Policy: frame-ancestors different-domain.com

Note that in the X-Frame-Options header you need to be more explicit and also specify the protocol.

ONLY LOAD IN IFRAMES ON A multiple SPECIFIC DOMAINs

What if your site needs to run on multiple domains? Unfortunately the X-Frame-Options headers is not so flexible, so you just can’t specify this. A possibility might be to detect which parent domain is used (e.g. through an explicit referrer parameter) and set the X-Frame-Options accordingly if allowed. Otherwise, you need to rely on Content-Security-Policy. In practice, that would mean that Internet Explorer (at least until IE11) won’t be able to give your users any safety regarding clickjacking. You can find out which browsers support Content-Security-Policy on http://caniuse.com/#feat=contentsecuritypolicy.

Content-Security-Policy: frame-ancestors example.com, *.example2.com
Conclusion

So, depending on your situation, all you need to do is add some JavaScript and one or two HTTP headers. No excuses! There are security vulnerabilities that are a lot harder to solve.

Just for the sake of compleness, I should mention that there are also ways users can protect themselves from the browser side through all kinds of browser plugins, but that’s a whole different story. Perhaps a topic for a new blogpost?

Clickjacking

2 thoughts on “Clickjacking

Leave a comment