SuperGeekery: A blog probably of interest only to nerds by John F Morton.

A blog prob­a­bly of inter­est only to nerds by John F Mor­ton.

Deter Clickjacking When You Can’t Set Security Headers By Using Javascript

Dont clickjack me

In this post we’ll talk about click­jack­ing, how to pro­tect your site vis­i­tor’s with robust sol­tu­ions, and a fall­back method when you’re unable to do it the nor­mal way. The need for this fall­back solu­tion is what inspired by a real-world sce­nario that I faced when a client need­ed a quick fix while the longer-term solu­tion was imple­ment­ed.

What is clickjacking

How do you pro­tect your users from click­jack­ing? First, you have to be aware of what click­jack­ing is. Let’s look at the Mozil​la​.org doc­u­men­ta­tion for a defin­tion.

Click­jack­ing is an inter­face-based attack that tricks web­site users into unwit­ting­ly click­ing on mali­cious links. In click­jack­ing, the attack­ers embed their mali­cious links into but­tons or legit­i­mate pages in a web­site.

It’s eas­i­er to under­stand what the means through an made-up exam­ple.

An example of clickjacking

Imag­ine this web­page at supergeekery.com was being hijacked from a sim­i­lar but mali­cious url, suppppergeeeekery.com. The mali­cious url could embed the legit­i­mate page with­in an iframe and then add addi­tion­al con­tent on top of the orig­i­nal con­tent. For exam­ple, the inter­net-bad-guy could add a but­ton to the page that says Sign up and get $1,000,000!” When a user clicks on the but­ton, a form would appear that col­lect­ed the user’s data for nefar­i­ous pur­pos­es. 😠

The X‑Frame-Options header

As the cre­ator of supergeekery.com, it’s easy for me to stop inter­net-bad-guys from embed­ding supergeekery.com into an iframe by set­ting the site’s head­ers. The screen­shot below shows the head­ers that have been set on this web­page. There are quite a few head­ers, but I’ve high­light­ed one in par­tic­u­lar called X‑Frame-Options, which has been set to SAME­O­RI­GIN. This head­er is a rule that a brow­er will obey that says this web­page can only be used with­in an iframe on the same domain that it orig­i­nal­ly exists on.


Supergeekery iframe options headers

The X-Frame-Options header set to "SAMEORIGIN"


The Content-Security-Policy header

Mod­ern browsers sup­port a pow­er­ful and nuanced option with the Con­tent-Secu­ri­ty-Pol­i­cy head­er. This is also an effec­tive solu­tion, but this head­ers is more com­pli­cat­ed to set up. I don’t need that lev­el of com­plex­i­ty so I still use the X‑Frame-Options head­er. Use which ever solu­tion works best for you.

Setting headers

Set­ting head­ers can be com­pli­cat­ed because they are set on a serv­er lev­el and are depen­dent on how you’re build­ing your site. That means I can’t show you how to set head­ers for your project, but I can sug­gest some next steps. You might con­fig­ure them in your Apache con­fig­u­ra­tion, your Nginx con­fig­u­ra­tion, or maybe with a dynam­ic serv­er side lan­guage, like PHP or Node. On this site, I’ve set them up through my Nginx con­fig­u­ra­tion.

Testing

You can see your head­ers in your Dev­Tools, as I showed in the first screen­shot. You can also use a test­ing tool to val­i­date that they’re set cor­rect­ly. Let’s take a look at this site in one of the test­ing tools: https://iframetester.com/?url=https://supergeekery.com


Supergeekery iframe tester

SuperGeekery.com, shown in iframetest.com, can't be embedded in an iframe.


A fallback solution

As a devel­op­er, you may find your­self in sit­u­a­tions where you can’t set your head­ers as you want. This is often because a clien­t’s infrus­truc­ture is man­aged in a way that pro­tects that type of access. While you’re work­ing on a robust solu­tion, you can still add some pro­tec­tion for your users using Javascript.

The fol­low­ing Javascript code block can be insert­ed into your pages and will attempt to pre­vent a page from being embed­ded in an iframe on anoth­er domain. It will allow your page to be put with­in an iframe on the same domain. I did that inten­tion­al­ly. You may have valid rea­son for using an iframe on your own con­tent.


<script>
  document.addEventListener("DOMContentLoaded", function () {
    if (top.location !== location) {
      document.body.innerHTML = ''; // Clears the existing content
      var noticeDiv = document.createElement('div'); // Create a new div element
      noticeDiv.innerHTML = 'Warning: This page cannot be embedded on another domain.'; // Set the message
      document.body.appendChild(noticeDiv); // Append the div to the body
    }
  });
</script>

I don’t intend to sug­gest that this is all you need to do, but it will help while you get your head­ers set prop­er­ly.