Table of Contents
Clickjacking
It involves tricking a user into clicking something different from what they think it is. Let’s create a straightforward example with it.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>A malicious website</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button>Just a button</button>
<iframe id="banking-site-iframe" src="https://your-bank.com"></iframe>
</body>
</html>
Above, we have both an iframe and a button. The user might intend to click the button, but it might be a trick. Let’s also look at the CSS that this website has.
style.css
#banking-site-iframe {
position: absolute;
top: 0;
left: 0;
opacity: 0%;
}
The above CSS positions the iframe on top of the button. When the users click on the button, they click on the iframe instead. If the attackers position the iframe carefully, they might be able to trick the user into clicking the delete account button.
X-Frame-Options header
The most straightforward way of dealing with the above issue is disallowing other websites from embedding our page through iframes. To do that, we can use the X-Frame-Options response header.
The X-Frame-Options response header specifies whether we allow the browser to render our page in <iframe>, <frame>, <object>, and <embed> elements. It can have one of the three values:
- X–Frame–Options: sameorigin
- by using the sameorigin value, we allow our page to be embedded by websites within the same origin.
- X–Frame–Options: deny
- with the deny value, we prevent all websites from embedding our page.
The X-Frame-Options header has a few shortcomings we need to know. Unfortunately, older versions of Firefox have a bug where sameorigin would not work correctly in all cases.
Also, with X-Frame-Options, we can’t allow a particular website to embed our page. Therefore, the X-Frame-Options header might not fit our use case. An obsolete directive allow–from supports that, but we can’t rely on browsers to implement it. Therefore, if we want to allow just certain websites to embed our page, we can’t achieve that with X-Frame-Options.
Setting the X-Frame-Options header
If we use Node.js with Express to serve our content, setting the X-Frame-Options header is very straightforward.
#banking-site-iframe {
position: absolute;
top: 0;
left: 0;
opacity: 0%;
}
If we use PHP
|
If you are using apache web server, you can directly set in httpd.conf also.
<Directory />
...
Header always set X-Frame-Options "DENY"
</Directory>
For example, it can set the X-Frame-Options header for us, and much more.
Content-Security-Policy header
It is a powerful header that allows us to control what resources the browser can load and execute.
One of the available directives is the frame–ancestors. It allows us to specify a list of pages that can embed our website. There are a few values that we should cover.
- Content–Security–Policy: frame–ancestors ‘none’
- works similarly to X–Frame–Options: deny
- Content–Security–Policy: frame–ancestors ‘self’
- works similarly to X–Frame–Options: sameorigin
- Content–Security–Policy: frame–ancestors https://secure-website.com https://another-secure-website.com
- allows a list of websites to embed our page
- Content–Security–Policy: frame–ancestors ‘self’ https://secure-website.com
- allows embedding our page both by websites within the same origin and an external trusted website
Content-Security-Policy vs. X-Frame-Options
By looking at the above, we can say that the frame–ancestors directive is more flexible than the X-Frame-Options header.
According to caniuse.com, the browsers of roughly 93% of users support the frame–ancestors directive. On the other hand, caniuse.com claims that more than 96% of users use browsers that support the X-Frame-Options header. Therefore, to be safe, we can set up both the Content-Security-Policy and the X-Frame-Options headers.