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.