Almost every web page has links which open in a new tab leaving the other web page still available. For example, news agencies will tweet about recent events on Twitter: the tweet contains a short description of the article and a link to see the full story on their web page.
By default, links in HTML are opened in your current tab and this is usually helpful. After all, as a web page owner, you want people to stay on your web page and not get distracted by too many open tabs or other web pages. People?s attention is limited which is why you want them to focus on your content. Besides, links which open in a new tab are also bad for accessibility if you don?t provide hints and ARIA attributes.
There are still cases where you want or need to link to external pages. The aforementioned news agency example is a good example: news agencies want people to read news articles on their web pages and not on external web pages like Twitter because you need the ad revenue from visitors. This is why Twitter allows its users to use links in their tweets. Usually, opening links in a new tab is used when the link does not belong to you but to a third party.
How to fall right into the trap
So, how do you make a link open in a new tab in HTML? This is easy (maybe too easy), we don?t even need to write JavaScript code. All we need to do is adding the target=?_blank? attribute to your anchor and we?re done.
<a href=?https://sedeo.net? target=?_blank?>Go to Sedeo</a>
Now, every time someone clicks this link, the web page (in this case Sedeo) will be opened in a new tab.
Let?s pretend that the web page I?m linking to in the example is safe (I know it?s safe because I created it). But maybe the external web page belongs to a third party (e.g. a service provider or a customer). Or maybe you allow user generated content on your web page (e.g. comments in which users can use links to external web pages). In these cases, you aren’t in control of these linked web pages and you don?t know what the people behind these are doing there.
If you?re unlucky, you might have fallen right into a trap and unintentionally created a security and performance issue.
You probably did not see that coming.
Security issue: ?tabnabbing?
By default, when you open a web page in a new tab by clicking on a link with target=”_blank”, this page has now limited access to the linking page. The most critical security aspect I can think of is that you can change window.opener.location of the linking page. For example: a shady news web page uses Twitter to spread some stories. The user on Twitter sees the tweet, then he clicks to read the full story on the shady news web page which is opened in a new tab. Meanwhile, the shady news page can use this embedded script on their page
window.opener.location = ?http://fake-twitter.com?
and now, the user is not on Twitter anymore but this fake Twitter-lookalike web page. This fake page looks exactly like Twitter and it asks to login because you were signed out for some reason. The user, not thinking too much about it, enters his credentials and submits the form.
You’ve just sent your private credentials to someone who can now login in your Twitter account and do anything like quickly changing the credentials.
This kind of phishing method is called (reverse) tabnabbing. As a reminder, here?s the definition of phishing from Wikipedia:
Phishing is the fraudulent attempt to obtain sensitive information such as usernames, passwords, and credit card details (and money), often for malicious reasons, by disguising as a trustworthy entity in an electronic communication.
Impact on browsing performance
Interestingly, target=”_blank” has also an impact on your web browsing performance which I have observed firsthand. Every major modern browser like Chrome and Firefox is multi-process. Due to the synchronous cross-window access the DOM gives us via window.opener, windows launched via target=”_blank” end up in the same process & thread. The same is true for iFrames and windows opened via window.open.
A click on a tile will open the page in a new tab.
As a side-project, I developed a personal ?start page? which looks and behaves similar to Opera?s Speed Dial. When clicking one of the tiles, the web page is opened in a new tab (each link has target=?_blank?). I observed that my web browsing was slower for some reason and I suspected it must have something to do with my own page because I didn’t encounter this issue when I didn’t use my own page. After I found out about this issue with target=?_blank?, I changed my code and the performance slowdown was gone.
Which popular web pages are vulnerable?
The issue is well known but has recently became more known in the last few years. Here are some web pages in which this tabnabbing approach works (as of the time of writing this article). I hope these web pages which are used by millions fix this issue quickly:
- Ebay.com (though the security impact should be reduced since most external links run in an iFrame)
- Heroku.com
- My Nintendo
Some web pages which successfully block this tabnabbing approach:
- GitHub.com
- Slack.com
- Twitter.com
- Facebook.com
- united-domains Webmailer (they fixed it when I notified them. That?s the spirit!)
- dev.to (they fixed it quickly when I notified them)
There are two solutions to fix these problems. The first solution is to add rel=”noopener noreferrer” attribute to every link with target=”_blank”. noopener is the necessary value to ensure linked pages do not have access to the linking page. Use this approach when you have access to the HTML code and when there are not many occurrences to fix this by hand. Some CMS like WordPress (? 4.7.4) do this automatically so you don?t need to take action. The (now secure) example above would now look like this:
<a href=?https://sedeo.net? target=?_blank? rel=?noopener noreferrer?>Go to sedeo</a>Browser support for noopener according to caniuse.com
Browser support for noopener is pretty good with modern browsers, Microsoft browsers like Internet Explorer being an exception like usual. However, Microsoft Edge supposedly doesn’t support window.opener for target=”_blank” links.
The second approach is to use JavaScript to fix this issue. Use this when you?re managing a CMS or if there are too many occurrences to fix all those links by hand.
var otherWindow = window.open();otherWindow.opener = null;
If you allow user-generated content (e.g. a social network), please make sure to sanitize links. Twitter automatically adds rel=”noopener” to every link in a tweet.
UPDATE: identify insecure static/dynamic links early
You can identify insecure links early if you add a lint rule. This way, you will be able to identify insecure links in your HTML. This linter would . Here is the implementation of such a rule for the popular HTMLHint linter:
module.exports = (HTMLHint) => { HTMLHint.addRule({ id: ‘noopener-external-links’, description: ‘Links with target=”_blank” must have a rel=”noopener noreferrer” attribute to prevent reverse tabnabbing.’, init: function (parser, reporter) { var self = this; parser.addListener(‘tagstart’, function (event) { var tagName = event.tagName.toLowerCase(); var col = event.col + event.tagName.length + 1; var mapAttrs = parser.getMapAttrs(event.attrs); if (tagName === ‘a’ && mapAttrs.target && (!mapAttrs.rel || mapAttrs.rel.indexOf(‘noopener’) === -1 || mapAttrs.rel.indexOf(‘noreferrer’) === -1)) { reporter.warn(‘Prevent reverse tabnabbing of external links by providing rel=”noopener noreferrer” attribute.’, event.line, col, self, event.raw); } }); } });};
To identify whether opening a page in a new window through JavaScript is insecure, you can write a ESLint / TSLint rule which bans the use the direct usage of window.open in favor of a custom function which resets the opener.
Dynamically created links (e.g. through jQuery) will not be identified by linters. One way to identify them is to repeatedly scan the DOM for insecure links when you are developing locally. Here is a basic way to do that in plain JavaScript:
if (!environment.production) { setInterval(() => { const links = Array.from(document.querySelectorAll(‘a[target]’)); for (let link of links) { const target = link.getAttribute(‘target’); if (target && (!link.getAttribute(‘rel’) || link.getAttribute(‘rel’).indexOf(‘noopener’) === -1)) { console.error(`Unsafe link ${link} is vulnerable to reverse tabnabbing.`); } } }, 5000);}
Conclusion
Interestingly, Google does not think of this as a major security issue.
In my view, there are no major downsides in applying the first solution (adding rel=”noopener” to every target=”_blank” link). This issue shows how easy it is to create loopholes in your web page security. Also, the performance impact of target=?_blank? links may also be non-negligible for web applications with a high need for performance. If you are a web developer, I?d advise you to check your code to eliminate this issue.
Thank you for reading my thoughts on this issue. I would love to hear about your thoughts in the comments.
Sources:
- https://jakearchibald.com/2016/performance-benefits-of-rel-noopener/
- https://www.jitbit.com/alexblog/256-targetblank—the-most-underestimated-vulnerability-ever/
- https://mathiasbynens.github.io/rel-noopener/
- https://www.owasp.org/index.php/Reverse_Tabnabbing