External Link Security
rule · new-tab
When a link opens a new browser tab with target="_blank", the new page receives a reference to the opener's window object via window.opener. A malicious destination can exploit that behavior in a reverse tabnapping (opens in a new tab) flow unless you explicitly add rel="noopener" (opens in a new tab) or rel="noreferrer" (opens in a new tab).
Code Example
1. User is on bank.example.com
2. User clicks a link to malicious.com (opens in new tab)
3. malicious.com runs: window.opener.location = 'https://fake-bank.example.com/login'
4. User switches back to the original tab
5. Tab now shows the phishing site — user thinks the bank session expired and types credentialsWhy It Matters
A malicious site opened via target='_blank' can use window.opener.location to silently redirect your original tab to a phishing page — the user switches back and sees a fake login screen on what appears to be your domain.
The Fix
❌ Vulnerable to reverse tabnapping
<a href="https://example.com" target="_blank">Visit Example</a>
✅ Safe — no access to window.opener
<a href="https://example.com" target="_blank" rel="noopener noreferrer">Visit Example</a>React / JSX
❌ Vulnerable
<a href="https://external.com" target="_blank">External Link</a>
✅ Safe
<a href="https://external.com" target="_blank" rel="noopener noreferrer">
External Link
</a>Understanding the rel Values
| Value | What it does |
|---|---|
noopener | Sets window.opener to null in the new tab — the opened page cannot access the original window |
noreferrer | Suppresses the Referer HTTP header sent to the destination; also implies noopener |
| Both together | Maximum privacy + security: no opener access, no referrer sent |
Recommendation: Use both noopener noreferrer for external links. Use noopener alone if you need the referrer for analytics on same-origin links.
Browser Behavior
As of Chrome 88 and Firefox 79, cross-origin target="_blank" links implicitly get noopener behavior. However:
- Same-origin
_blanklinks are not implicitly protected - Older browsers still require the explicit attribute
noreferreris never added implicitly
For these reasons, always add the attribute explicitly.
Automated Enforcement
ESLint (React projects)
pnpm add -D eslint-plugin-react// .eslintrc.json
{
"rules": {
"react/jsx-no-target-blank": ["error", {
"allowReferrer": false,
"enforceDynamicLinks": "error"
}]
}
}Finding Violations in Existing Code
# Search HTML files
grep -rn 'target="_blank"' ./src --include="*.html" | grep -v 'new-tab'
# Search JSX/TSX files
grep -rn 'target="_blank"' ./src --include="*.jsx" --include="*.tsx" | grep -v 'new-tab'Internal Links
Internal links (same domain) are generally safe to open in new tabs without noopener because you control both pages. However, adding it is still a best practice for consistency:
<!-- Internal link — window.opener risk is lower, but still good practice -->
<a href="/docs/guide" target="_blank" rel="noopener">Open Docs</a>Exceptions
- A weaker form control is only acceptable when the business requirement and compensating controls are documented explicitly.
- If the flow is already transport-insecure, inaccessible, or externally embedded in a way that changes the threat model, fix that stronger issue first.
- False positives are common on demo, sandbox, or intentionally constrained flows, but they should still be bounded and clearly labeled.
Verification
Automated Checks
- Test the affected flow in a production-like environment, not just local development.
- Document any intentional exceptions explicitly.
Manual Checks
- Inspect the final HTTP response or browser behavior to confirm the control is actually enforced.
- Verify third-party integrations or embeds still work after the restriction is applied.