Deter Clickjacking When You Can’t Set Security Headers By Using Javascript
In this post we’ll talk about clickjacking, how to protect your site visitor’s with robust soltuions, and a fallback method when you’re unable to do it the normal way. The need for this fallback solution is what inspired by a real-world scenario that I faced when a client needed a quick fix while the longer-term solution was implemented.
What is clickjacking
How do you protect your users from clickjacking? First, you have to be aware of what clickjacking is. Let’s look at the Mozilla.org documentation for a defintion.
Clickjacking is an interface-based attack that tricks website users into unwittingly clicking on malicious links. In clickjacking, the attackers embed their malicious links into buttons or legitimate pages in a website.
It’s easier to understand what the means through an made-up example.
An example of clickjacking
Imagine this webpage at supergeekery.com
was being hijacked from a similar but malicious url, suppppergeeeekery.com
. The malicious url could embed the legitimate page within an iframe and then add additional content on top of the original content. For example, the internet-bad-guy could add a button to the page that says “Sign up and get $1,000,000!” When a user clicks on the button, a form would appear that collected the user’s data for nefarious purposes. 😠
The X‑Frame-Options header
As the creator of supergeekery.com
, it’s easy for me to stop internet-bad-guys from embedding supergeekery.com
into an iframe by setting the site’s headers. The screenshot below shows the headers that have been set on this webpage. There are quite a few headers, but I’ve highlighted one in particular called X‑Frame-Options, which has been set to SAMEORIGIN. This header is a rule that a brower will obey that says this webpage can only be used within an iframe on the same domain that it originally exists on.
The Content-Security-Policy header
Modern browsers support a powerful and nuanced option with the Content-Security-Policy header. This is also an effective solution, but this headers is more complicated to set up. I don’t need that level of complexity so I still use the X‑Frame-Options header. Use which ever solution works best for you.
Setting headers
Setting headers can be complicated because they are set on a server level and are dependent on how you’re building your site. That means I can’t show you how to set headers for your project, but I can suggest some next steps. You might configure them in your Apache configuration, your Nginx configuration, or maybe with a dynamic server side language, like PHP or Node. On this site, I’ve set them up through my Nginx configuration.
Testing
You can see your headers in your DevTools, as I showed in the first screenshot. You can also use a testing tool to validate that they’re set correctly. Let’s take a look at this site in one of the testing tools: https://iframetester.com/?url=https://supergeekery.com
A fallback solution
As a developer, you may find yourself in situations where you can’t set your headers as you want. This is often because a client’s infrustructure is managed in a way that protects that type of access. While you’re working on a robust solution, you can still add some protection for your users using Javascript.
The following Javascript code block can be inserted into your pages and will attempt to prevent a page from being embedded in an iframe on another domain. It will allow your page to be put within an iframe on the same domain. I did that intentionally. You may have valid reason for using an iframe on your own content.
<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 suggest that this is all you need to do, but it will help while you get your headers set properly.