target="_blank" 单独使用不安全,必须搭配 rel="noopener noreferrer",否则新页面可通过 window.opener 劫持原页面跳转、篡改dom或导致卡顿;noopener 切断引用是安全底线,noreferrer 防referer泄露,二者不可替代且须显式声明。

target="_blank" 单独使用不能安全打开新窗口,必须搭配 rel="noopener noreferrer",否则现代浏览器可能降级行为或保留危险的 window.opener 引用。
为什么只写 target="_blank" 会出问题
浏览器新开标签页时,默认让新页面持有对原页面的 window.opener 引用。这意味着它能执行 window.opener.location = 'https://evil.com' 直接劫持你的原始页面——这不是理论风险,而是默认行为。
- 同源页面下,还能读取 DOM、Cookie、调用 JS 函数
- 即使跨域,也能阻塞原页面主线程(共享渲染进程),导致卡顿
- Chrome 92+、Firefox 88+、Safari 15.4+ 会主动拦截或静默降级:不加
rel="noopener"时,部分场景直接退化为当前页跳转 - 控制台出现
Blocked a frame with origin ... from accessing a cross-origin frame就是已被拦截的信号,说明漏洞已存在
rel="noopener" 和 rel="noreferrer" 到底怎么选
rel="noopener" 是硬性要求,rel="noreferrer" 是可选增强。二者作用不同,不能互相替代。
-
rel="noopener":切断window.opener引用,解决安全与性能问题;所有现代浏览器(Chrome 49+、Firefox 52+、Safari 10.1+)均支持 -
rel="noreferrer":额外移除Referer请求头,防止目标站获知来源 URL;但旧版 Safari 对它的opener隔离不完整,不能代替noopener - 不要只写
rel="noreferrer"—— 这在 Safari 9 或某些 WebView 中仍暴露window.opener - 也不必写成
rel="noopener noreferrer noopener"—— 冗余,noopener已隐含自身语义
动态生成链接时最容易漏掉什么
Vue/React 模板里写死的链接容易补全,但 JS 动态拼接、API 渲染、富文本输出等场景,rel 极易被忽略。
- Vue 中用
:rel="isExternal ? 'noopener noreferrer' : ''",但若isExternal为false或undefined,就漏了防护 - React JSX 里写
rel={external ? "noopener noreferrer" : undefined},但undefined不会渲染属性,等于没写 - 服务端模板(如 Jinja、EJS)中硬编码
target="_blank"却未同步加rel,输出 HTML 就带漏洞 - CMS 富文本字段常过滤
rel属性,需配置白名单或后端清洗逻辑强制注入
兼容旧环境的 fallback 方案
如果项目必须支持 Safari 10.0 或 Android 4.4 WebView 等老旧环境,rel="noopener" 可能被忽略,此时需降级到 window.open()。
- 用
window.open(url, '_blank', 'noopener')—— 注意这里的noopener是 features 字符串参数,不是 HTML 属性,IE11 也支持 - 避免只依赖
target="_blank"在 WebView 场景(如微信内置浏览器),它们普遍无视该属性 - 不要试图用
target="_blank_"或target="blank"“绕过”,这些会被当作命名窗口处理,可能复用已有标签页,造成意外覆盖
真正关键的不是“能不能打开新窗口”,而是“打开之后原页面是否还安全”。只要漏掉 rel="noopener",哪怕只是静态页面里一个普通外链,攻击面就长期存在——Lighthouse 会标为严重问题,审计工具也会直接报红。这个细节没法靠事后测试发现,只能靠编码规范和自动化检查卡死在上线前。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











