target="_blank" 单独使用存在 tabnabbing 安全风险,因新页面可通过 window.opener.location 劫持原页;必须添加 rel="noopener" 切断 opener 引用,rel="noreferrer" 为可选隐私补充,二者不可替代。

必须加 rel="noopener noreferrer",否则 target="_blank" 会让新页面拿到 window.opener 引用,直接劫持原标签页——这不是漏洞预警,是浏览器明确允许的行为。
为什么只写 target="_blank" 就危险
新打开的页面默认能通过 window.opener 访问并修改原始页面,哪怕跨域也生效。攻击者只需一行脚本就能把你的登录页重定向到钓鱼站点:
window.opener.location = 'https://fake-bank-login.example'
用户切回原标签时毫无察觉,继续输入密码。这种攻击叫 Tabnabbing,已在真实钓鱼事件中复现多次。
- 不依赖同源策略,
window.opener在跨域下依然可读写location - 旧版 Safari 和 IE11 不支持
rel="noopener"的自动降级,必须显式声明 -
rel="noreferrer"单独使用在 IE11 中完全无效,它只隐藏 Referer,不切断 opener
rel="noopener" 和 rel="noreferrer" 各管什么
noopener 是安全底线,noreferrer 是隐私补充,二者目的不同,不能互相替代:
-
rel="noopener":强制新页面的window.opener === null,彻底阻断 JS 反向控制 -
rel="noreferrer":阻止 HTTP 请求头中发送Referer,避免泄露带 token 的路径或站内结构 - 现代浏览器中,
noopener还能减少内存占用,让原页面更快进入后台冻结状态
如果只要安全防护,rel="noopener" 已足够;但外链场景下,建议始终组合使用。
服务端或富文本中自动补全 rel 属性
人工补容易漏,尤其在用户评论、CMS 输出、Markdown 渲染等动态场景。应在输出前统一处理:
- 正则匹配所有含
target="_blank"且不含rel="noopener"的<a></a>标签 - 若原无
rel,插入rel="noopener noreferrer" - 若已有
rel="nofollow",合并为rel="nofollow noopener noreferrer",注意空格分隔 - 避免覆盖已存在的合法
rel值(如rel="stylesheet"不会出现在<a></a>上,但rel="author"等需保留)
前端也可用 DOMParser 或 innerHTML + 正则做二次清洗,但服务端拦截更可靠。
JavaScript 模拟 target="_blank" 时怎么防劫持
用 onclick="window.open()" 替代原生 target 并不能绕过风险——若未显式传入 opener: null,部分浏览器仍保留 opener 引用:
- ❌ 危险写法:
onclick="window.open('https://x.com', '_blank')" - ✅ 安全写法:
onclick="window.open('https://x.com', '_blank', 'noopener')" - 更稳妥的做法仍是优先用原生 HTML +
rel,语义清晰、无需 JS、兼容性更好
真正容易被忽略的是:即使你没写 target="_blank",某些第三方组件(如富文本编辑器、评论插件、分享按钮 SDK)可能悄悄注入带该属性的链接,且不带 rel。得定期审计 DOM 输出或加 CSP script-src 限制。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











