What all Developers need to know about: Clickjacking

Posted by Martijn van Lambalgen on January 17, 2018

— This post is part of a series of blog post about all kinds of Security topics for Developers —

Clickjacking is still one of those amazingly simple attacks that are also easy to prevent. That is, if you know what clickjacking is, because considering the amount of websites that are vulnerable, not many developers know about this.

In a clickjacking attack, an attacker attempts to ‘hijack’ clicks by making the user think he is clicking something else. The basic idea here is that the attacker loads the thing he wants you to click on in an invisible iframe and then shows you something else. For example, you may see a button ‘Click here to get a Free iPad’, but when trying to click it the button, the click-event goes to a ‘Transfer $1000,- from my creditcard’ button in the invisible iframe. Clickjacking may cause all kinds of harm to the user. E.g. the hacker may get access to your webcam, steal money, send emails on your behalf, or worse… It is possible to hijack basically any type of event in the browser (like mouse events or key strokes) if the website that executes that action is not properly secured.

Clickjacking is a special form of ‘Window UI redress’. The basic idea is always to show the original web page in which the actions are performed, but make it look different (redress) to make the victim more likely to execute the action. A hacker can ‘hijack’ user actions like mouse clicks, key presses or other events, and use them for a different purpose than the user thinks it is. Usually the user won’t even know that he loaded the website until after the harm was done.

Clickjacking

Example

A (simplified) code example probably gives you a better idea:

<html>
    <body> 
      <iframe src="vulnerable-site.com/dangerous/function/" 
              style="opacity: 0;"></iframe> 
      <button>Click for free iPad</button> 
    </body> 
</html>

A hacker could create this page to induce users to click a link on the vulnerable site. It first contains an invisible iframe (opacity: 0) which loads the vulnerable site. And then there’s a button that induces the user to click on it. The user will think he’s clicking the button, but since the iframe came first, the click-event is handled by the iframe and the dangerous function gets executed.

This example can further be enhanced by styling the iframe, and moving the desired functionality in view. Furthermore the page can be zoomed, so the button is essentially the only thing that’s in the iframe. It is also possible to use JavaScript to let the iframe be 1×1 px in size and let it always hover below the mouse cursor, so it doesn’t matter where the user clicks. Also, it’s possible to create a simple game to let the user click multiple times, and maybe use the keyboard to enter text. This guy shows a great example that illustrates the power of clickjacking to get access to your webcam.

Prevention

The idea behind clickjacking prevention is that you tell the browsers whether your site can be loaded in an <iframe> (or <frame>, <object>, <embed> or <applet>) or not, and if you want to allow it, which domains can embed your site. There are also client side mitigations, like plugins that warn you when clicking invisible content. However, as a developer you shouldn’t want to rely on your users installing such plugins, so we’ll focus on the server side mitigations.

There’s different options:

Never let your site load in any iframe

This is the easiest situation. Send these two HTTP headers in each and every response to the browser:

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

Most browsers listen to the first header, but it is a bit limited. It is also possible to use Content Security Policy to be more specific, but support for that header is still not complete. So it’s best to add both headers. Since there are (mostly legacy) browsers that don’t support any of this, it’s good to also add a JavaScript snippet to check if your site is indeed the topmost site. OWASP has a whole history of all the versions of anti-clickjacking scripts, but the ‘best for now’ is this:

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

Just don’t rely on the script alone, because there’s all kind of ways for attackers to disable the JavaScript protection. E.g. by loading your site in a sandbox iframe, or using the anti-XSS protection of the browser against itself.

Only let your site load on a specific domain

Usually you’ll want your site to load in an iframe on the same domain, in which case you can just use the following HTTP headers in your responses:

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

If you want to allow loading on a different domain, use this syntax:

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

Note that X-Frame-Options is a lot more strict, so you need to specify the scheme as well as the full domain. With Content Security Policy it is possible to leave the scheme and even use wildcards.

Allow your site to load on multiple specific domains

In this case it easiest to just go for the Content Security Policy header, because that allows you to specify the domain in a list, or with the use of wildcards:

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

The drawback of Content Security Policy is that many older browsers (including Internet Explorer up until verion 11) don’t support it.

Unfortunately X-Frame-Options doesn’t support multiple domains. If you really don’t want to let IE11 users down, you can check the referrer header on the server, and if it appears in your whitelist, you can put that specific domain in the X-Frame-Options header.  It is a bit nasty, but it is your best shot.

Check the support for both headers in different browsers here:

About the author: Martijn van Lambalgen

Software engineer at TOPdesk interested in Software Architecture, Continuous Delivery and Web Security. Also wine drinker, good food lover, amateur baker and pleasure-seeker in general

More Posts