- Published on
Tabnabbing Attacks and Prevention
- Teo Selenius
- Follow @TeoSelenius
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.
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:
The malicious website can then get a window handle to your website via the
window.opener property like so:
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
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:
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.
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.
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
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.
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
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.
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
sandboxattribute 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.