What are tabnabbing attacks?
Tabnabbing attacks enable a malicious website to suddenly redirect a legitimate page to the attacker's page.
They can be an effective tool in phishing attacks, so let's see how you as a developer can safeguard your users against the attack.
Let's first look at the different forms of tabnabbing and then we'll discuss the defenses. You will get to play with interactive demos throughout this article, or just watch them in animated GIFs if that's your preference.
The original article can be found here
A hole in the Same Origin Policy
Before we proceed, I should say two words about the same-origin policy.
For the sake of this article, it is enough for you to know that the same-origin policy is the browser security mechanism that isolates different websites from each other.
It ensures that, e.g., google.com cannot read your Facebook feed, even though you have Google and Facebook open in the same browser.
And while the same-origin policy restricts many things, it has many "holes" because websites do need to interact with each other to some extent.
And the hole that we are most interested in right now is the following:
- If websites A and B are from different origins
- And website A manages to get a window handle to website B
- Then website A is allowed to redirect website B's window to any URL address at any given time
This hole is the reason tabnabbing attacks exist, as you'll soon see.
If you want to learn more about the same-origin policy, we have an in-depth article right here.
How do tabnabbing attacks work?
There are a couple of ways in which a malicious website could get the window handle to your website. These are the most common.
1. Tabnabbing by the malicious page opening a window
Perhaps the easiest and most reliable way for another website to get a window handle to your website's window is for the malicious website to open it via window.open
like so.
var windowHandle = window.open('https://goodsite.example')
// sleep for some time, and suddenly...
windowHandle.location.replace('https://hacked.example')
2. Reverse tabnabbing by the good site opening a window
Another possibility is the other way around, that is, your website opens the malicious website via window.open
like so:
window.open('https://evil.example')
The malicious website can then get a window handle to your website via the window.opener
property like so:
window.opener.location.replace('https://hacked.example')
3. Reverse tabnabbing via links
The second reverse tabnabbing variant is if you link to a malicious/compromised website with a target="_blank"
so that the website will open in a new tab/window. In this case, the linked website can refer to the previous window via window.opener
.
window.opener.location.replace('https://hacked.example')
4. Reverse tabnabbing via frames
The third reverse tabnabbing method is if your website loads another website in an IFRAME
. For example, advertisements can work this way. In this case, the malicious website in the frame can get a window handle to the parent window using the window.parent
property like so:
window.parent.location.replace('https://hacked.example')
Try it!
Play around with the attacker's page here and the "good page" here.
I should mention that depending on your browser and configuration, the attacks might not work. We'll get to why at the end of this article. Just don't be surprised if that happens to you now.
It's also worth noting that if the CodeSandbox instances were hibernated, you might want to refresh the pages after waking up the applications to ensure that everything works as expected.
How to prevent tabnabbing attacks?
Preventing tabnabbing attacks is relatively straightforward.
- Implement a cross-origin opener policy
- Set the rel="noopener" attribute to your links
- Sandbox your frames
- Implement an isolation policy
Implement a cross-origin opener policy
There is a relatively new browser security feature called Cross-Origin Opener Policy
. You can use COOP to prevent the attack where a malicious website calls window.open
on your website and then sneakily redirects the user into the attacker's page.
Just return the following HTTP response header from your webserver. The browsers that support COOP will process-isolate your document, and potential attackers can't access your window anymore.
Cross-Origin-Opener-Policy: same-origin
Additionally, windows that your website opens via window.open()
will not be able to attack you anymore.
The cross-origin opener policy feature is (at the time of this writing) already supported by Firefox, Chrome, and Edge, but not by Safari or IE. You can check the up-to-date status here.
Read more about COOP here.
Set the rel="noopener" attribute to your links
You can link to other websites in new windows as long as you include the rel="noopener"
attribute in the a
-tag. Like so.
<a href="https://www.example.com" rel="noopener noreferrer"></a>
I added the noreferrer
as a bonus because it's a good practice to add even though it's not related to tabnabbing attacks. The noreferrer
will prevent information from the browser user's URL from leaking to the other website in the Referrer
-header. You can read more about that here.
The noopener
attribute is the crucial part here. It tells browsers not to give the link target a handle to your website's window by setting the window.opener
to null
.
You can read more about the noopener
attribute here.
Sandbox your frames
To prevent tabnabbing attacks from websites that you load in an iframe, all you need to do is sandbox the frame. Sandboxing is facilitated with the sandbox
attribute like so:
<iframe sandbox="allow-scripts allow-same-origin" src="https://www.example.com"></iframe>
By default, the sandbox
attribute limits many things. Most importantly, it prevents the content from navigating its top-level browsing context, that is, it stops the framed website from redirecting its parent.
However, it also blocks things like having an origin at all or executing scripts. In the example above, we granted those two so that the frame can operate as expected.
Implement an isolation policy with fetch metadata
Another relatively recent browser security feature is fetch metadata. It allows you to block HTTP requests on the server-side if they originate from unwanted websites or happen in suspicious contexts.
Isolation policies are an insanely effective security control against a multitude of cross-site/cross-window attacks. While it's not yet supported by Firefox or Safari, you can implement such a policy in a fully backward compatible manner and reap the benefits on the browsers that do support it.
I won't go into too much detail about isolation policies in this article. You can read a thorough treatment about them in this article which also features a complete example that you can run on CodeSandbox.
Try it!
Play around with the attacker's page here and the COOP-protected test page here.
Browsers try to fight tabnabbing
Modern browsers try to implement heuristics and better defaults to combat tabnabbing attacks.
For example, most browsers these days treat links that have target="_blank"
as rel="noopener"
by default unless you explicitly specify something else.
On Firefox the feature is enabled as long as you have dom.targetBlankNoOpener.enabled
enabled in about:config. Read about it here.
Chrome also has the same feature which you can read about here.
Ditto for Safari.
Also, browsers might prevent redirections or popups that appear suspicious or ask the user if they want to allow them.
However, you can't rely on the browser to keep your web application safe. Most of the attacks are not prevented out-of-the-box.
Conclusion
Tabnabbing attacks can be a severe threat, especially when used as part of a targeted phishing attack. Luckily there are simple steps that you can take to protect your web application from them:
- Implement a cross-origin opener policy
- Add the
rel="noopener"
attribute to the links on your website. - Add the
sandbox
attribute to iframes on your website. - Implement an isolation policy with fetch metadata.
Additionally, browsers are getting smarter in preventing tabnabbing attacks. Still, they're not quite there yet, so you as the developer will have to take care of implementing these security controls to protect your users.
Get the web security checklist spreadsheet!
☝️ Subscribe to AppSec Monkey's email list, get our best content delivered straight to your inbox, and get our 2021 Web Application Security Checklist Spreadsheet for FREE as a welcome gift!
Don't stop here
If you like this article, check out the other application security guides we have on AppSec Monkey as well.
Thanks for reading.