— This post is part of a series of monthly blog posts about all kinds of Security topics for Developers —
It’s been a while ago when I first heard about reverse tabnabbing. Initially I didn’t think much of it, because exploits are not always straightforward. At TOPdesk we fixed any potential issues some time ago, but recently it’s getting more attention. I always applaud it when security topics get more attention, and therefore I’d like to explain here how we approach this at TOPdesk.
“Reverse tabnabbing” you say? What are you talking about? Never heard of this thing. Okay, so apparently it hasn’t gotten enough attention yet. Reverse tabnabbing can easily lead to some nasty phishing attacks. I originally read this article by Mathias Bynens on rel=noopener. It explains (and demonstrates) that if a site has a link to an external domain, and when you click the link, the site on the other domain gets full control over the parent’s window object.
This might be a nice feature when creating some edit-dialog, so that after editing an entity it can automatically tell the window.opener page to reload: window.opener.location.reload(); However, in most cases you don’t want the child-page to know anything about the parent, much less control it.
Phishing attack
A classic example would be a message forum, in which a hacker posts a link to his own website. People visiting the forum click the harmless-looking link and will most likely see a page with some boring content. In the background this page will redirect the parent page to a fake login page, which looks the same as the login page the message forum uses. When the users go back to the message forum tab, they appear to be logged out. They don’t think much of it, and fill in their credentials. After submitting the credentials, they get harvested by the hacker and to avoid suspicion the user is automatically sent back to the forum’s main page.
This attack is actually worse than a regular phishing attack, because users don’t actively go to the fake login page. For this reason there’s no real trigger to check if the URL in the location bar is indeed authentic. Also, since TLS certificates are nowadays free of charge, the phishing site can look really secure and safe.
Recommendations
So, what’s the solution to avoid all this nastiness? Basically we should tell the browser to not make this window object available to child pages. Since it can be complicated and error-prone to decide this for each situation individually, we follow the same rule always!
<a href="https://example.com/" target="_blank" rel="noopener noreferrer">Link to external site</a>
Note the rel=”noopener noreferrer” part. The noopener value tells the browser to not make an opener object available to the linked website. Because some (older) browsers don’t support this, it’s best to add the noreferrer value. The noreferrer tells the browser not to make any referrer info available to the linked website.
If you don’t want to set the noreferrer on every link separately, it’s also possible to add an HTTP header to all your responses. We added the following HTTP header to our reverse proxies:
Referrer-Policy: no-referrer
For more info on this, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
External websites can also be created through JavaScript, so we also implemented a function to avoid issues there:
function openPopup(url, name, options){ // Open the popup and set the opener and referrer policy instruction var newWin = window.open(null, name, 'noopener,noreferrer,' + options); // Reset the opener link newWin.opener = null; // Now load the correct url newWin.location = url; }
Note that the URL is only filled in after the opener object is cleared. Setting the URL in the beginning allows for a theoretical window (or tab? 😉 ) of opportunity in which the child could theoretically do nasty things, before the opener is finally cleared.
Performance
There are also positive side-effects of this approach. Sometimes the child page uses a lot of resources for calculations. Jake Archibald explains that because of synchronous cross-window access, the browser uses the same process for both windows. So, a heavy child process may influence the performance of the parent window. By removing this cross-window link, most browsers will now use separate processes, which results in an improved performance for the parent page in these situations.
Awareness
Don’t forget to educate your fellow developers about this type of vulnerability. Since TOPdesk has many developers in several countries, creating awareness to avoid future problems is important. It’s hard to force everybody to use the same JavaScript function, or the same HTML template piece of code, so besides fixing the reverse tabnabbing vulnerability, it’s important to make people aware about this. This blog post is just one of many steps to achieve this.
Summary
Preventing reverse tabnabbing is not a difficult thing. All you need to do is clear the window.opener object, so the child window cannot control the parent window. Unless you have a good reason to allow otherwise, you should add the ‘noopener’ and ‘noreferrer’ options to the rel attribute of an anchor tag, and explicitly clear the window.opener when using JavaScript. To keep your code secure, it’s advisable to educate your developers on this too.
Want to ready more?
- https://www.owasp.org/index.php/Reverse_Tabnabbing
- https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Tabnabbing
- https://mathiasbynens.github.io/rel-noopener/
- https://jakearchibald.com/2016/performance-benefits-of-rel-noopener/
- https://medium.com/@jitbit/target-blank-the-most-underestimated-vulnerability-ever-96e328301f4c
Want to read more about security? Find all our security blogs here: https://techblog.topdesk.com/tag/security/