加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.1461.cn/)- 应用程序、AI行业应用、CDN、低代码、区块链!
当前位置: 首页 > 运营中心 > 网站设计 > 佳作 > 正文

还在用点击跳转?20年安全工程师告诉你这有多危险

发布时间:2026-10-15 08:17:06 所属栏目:佳作 来源:DaWei
导读:  跟产品吵。去年二月份,PM 把“点击跳转”写进 PRD 第三版修订标黄里,字体加粗,还加了个小火箭图标——我说你不如直接写“请用户手动刷新页面”,至少诚恳一点。他笑,说这是“最轻量的路由切换”。我盯着那行字看了十八

  跟产品吵。去年二月份,PM 把“点击跳转”写进 PRD 第三版修订标黄里,字体加粗,还加了个小火箭图标——我说你不如直接写“请用户手动刷新页面”,至少诚恳一点。他笑,说这是“最轻量的路由切换”。我盯着那行字看了十八秒,桌面那个没关的录屏在右下角闪着红点,帧率掉到十九帧,但谁也没动它。


  先扔结论后补细节。还在用点击跳转?不是慢,是活该被劫持。去年二月那个 case,前端勾选合并请求前删了三行 history.pushState 调用,加了一行 location.href = 'https://www.shaguniang.cn/order';后端顺手把 /order 接口的 X-Frame-Options 从 SAMEORIGIN 改成 ALLOW-FROM https://mkt.xxx.com;中间层 CDN 缓存策略又漏配了 Vary: User-Agent——这三件事单拎哪件都够上灰度拦截名单,合起来?直接让某个安卓 WebView 用户点进去之后,页面底部弹出一个带支付宝图标、但域名是 cdn-2023-gw[.]xyz 的悬浮窗。这个我敢打包票:不是钓鱼页仿制,是跳转过程中 DOM 已卸载、新页面 JS 尚未执行的十七毫秒空窗期,被 inject 了一段 inline script。Lighthouse 跑分截图发到群里没人回,就卡在那个 87 分的 Accessibility audit 上,连带把 color contrast 的 warning 都盖住了。


文章配图,仅供参考

  下午三点,我端着杯子去倒水,路过前端工位,他正换一根 Type-C 充电线——银色那根,接口处有点发黑。我说你线都换了三根了,怎么还没切 SPA?他说上头说“过渡期保持兼容性”。我差点把水泼在他键盘上。兼容个屁,兼容的是三年前的微信内置浏览器?还是兼容产品经理脑子里那个“点一下就换个页面”的幻觉?


  优胜劣汰。这个词不是褒义,是冷笑话。真正在淘汰的不是技术,是没在跳转前后校验 referrer、没封禁 document.referrer 为空时触发的跳转、没在 window.beforeunload 里做状态快照的人。我原以为只要加个 navigateTo 检查 origin 就够了,结果上线第三天,有个用户从企业微信内嵌 H5 点进来,document.referrer 居然是 empty string——但 location.href 又能正常跳,因为 WKWebView 前期绕过了 referrer policy。这个我说不准,得等 iOS 17.4 beta 的 WebKit 日志出来再看,但现在它已经在生产环境跑了四个月零七天。


  CDN 账单的事插一句:上季度多花了三百七十二 KB 的边缘计算费用,就为跑一个 checkJumpAuth 中间件。值不值?值。但 PM 问我能不能“不改前端,只后端加固”,我说那你不如让保安在公司门口检查每个访客的鞋底有没有藏跳转 payload。他愣了两秒,然后去改钉钉消息模板了。对面工程师换了一根充电线,这次是深空灰。


  说回那个安卓 WebView 案例。不是没做 CSP,CSP 头全加上了,nonce 也随 HTML 动态生成。问题是——用户点了跳转,页面瞬间 blank,JS 加载中,此时 CSP 还没生效。这中间的空白,比凌晨两点服务器自动伸缩缩容时的连接断开时间还长。我们抓包看到三十七次 DNS 查询,其中十五次是解析那些伪造的 msn[.]com 子域名,它们全走的是同一个上游 resolv.conf——不是劫持 DNS,是劫持了跳转生命周期本身。失败案例?太多了:某电商“立即支付”按钮跳转时被插入 iframe 加载虚假风控页;某教育平台课程列表页跳详情页,途中被塞进一个带 audio autoplay 的隐藏 video 标签,用来骗权限。主观判断:所有声称“我们跳转很干净”的人,都没打开过 DevTools 的 Network → Preserve log 下划线的那个复选框。


  KPI 折算的事提一嘴:安全部今年考核指标里有一项叫“首跳链路可验证性”,权重 12%,但没人定义什么叫“可验证”。我和架构组对了一个下午,最后定的是——必须支持在跳转目标页 onl oad 之前,通过 postMessage 向来源页回传一个含签名的跳转指纹。听起来很重?其实就多一行 verifyJumpToken() 调用。但前端说“影响白屏时间”。我说那你倒是把 loading 状态做成骨架屏啊,别让用户干瞪着“正在跳转…”四个字等两秒半。他敲了下回车,说“已提交 RFC-2024-0921”,然后去吃午饭了。我看了眼时间:中午十二点四十,他饭盒里是青椒炒肉,米饭粒粘在盒盖内侧。


  现在我看到任何带 onclick="location.href" 的代码,手就会抖。不是过敏,是条件反射。上周 review 一个登录态续期逻辑,发现后端返回的 redirect_url 是拼接的,参数里还带了个 utm_source=wechat ——我立刻让测试在微信里点,果然,在 URL 解析完成前的十三毫秒,注入脚本抢跑了。这事我没报 incident,只发了个 IM:“别碰 redirect_url 字段,它现在是个哑铃,一头写着 domain,一头写着 payload。”


  所以接下来怎么做?我不建议一刀切。先把所有带 target="_blank" 且没加 rel="noopener noreferrer" 的链接打上 tag;再把所有 location.href 赋值语句 grep 出来,逐条看上下文有没有 try/catch 包裹;最后——把 webpack 打包时自动生成的 prefetch link 插入位置,挪到 document.body appendChild 后面两百毫秒。做不到?那就继续用,只是下次审计报告里,“跳转向量风险等级”那一栏,别再填“低”。填“高-可控”,可控的意思是:你得知道哪台服务器在哪个时刻、哪行日志里,悄悄记录过那次跳转的 origin 和 destination hash 不一致。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!