一个CSS文件,掀翻Referer防线

admin 2026-09-19 04:42:41 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文揭示利用CSS文件或JS模块绕过Referer校验实现CSRF攻击的新方法。核心在于strict-origin-when-cross-origin策略下,子资源请求的Referer记录的是发起请求文档的origin,而非用户当前页面。攻击者通过上传恶意CSS或JS到目标站,诱导浏览器发起带目标站自身Referer的请求,从而绕过校验。文章提供两种变体利用链及实战检测清单,建议关注文件上传功能并评估SameSite上下文影响。 综合评分: 85 文章分类: WEB安全,漏洞分析,渗透测试,红队


一个 CSS 文件,掀翻 Referer 防线

Ots安全

2026年9月17日 12:41 广东

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

威胁简报

恶意软件

漏洞攻击

一、先说结论:你信错了 Referer

Referer 校验的底层假设只有一句话:只有我自己网站的页面,才能发出带着我家域名的请求。

听起来没毛病,对吧?

问题出在浏览器身上。它悄悄改了 Referer 的”署名规则”,而大多数人根本没细看过这份规则。🎯

Chrome、Firefox、Safari 现在的默认引用策略,都叫 strict-origin-when-cross-origin。它的行为分三种:

  • 同源请求

    :Referer 带完整 URL,路径参数一个不少

  • 跨源请求

    :Referer 只带 origin(协议 + 主机 + 端口)

  • HTTPS 降级到 HTTP

    :Referer 干脆不发

图1:strict-origin-when-cross-origin 策略下 Referer 的三种取值(本文自制示意图)

但真正致命的不是这三分支,而是另一句话:

Referer 记录的,是”发起这次请求的那份文档”的 origin,不是”用户此刻正在看的那个页面”的 origin。

再读一遍这句话。这两者平时是同一个东西,所以你从没察觉区别。

可一旦发起请求的,是一个被链接进来的子资源——比如一份 CSS 文件、一个 JS 模块——事情就完全变了味。

二、变体一:上传一个 CSS,让目标站自己给自己发请求

来走一遍这条利用链。⚔️

假设目标站 B 有两个特点:

  1. 有一个改状态的接口(比如发帖),只靠 Referer 校验防护
  2. 允许用户上传文件,且文件按 Content-Type: text/css 原样返回

攻击者的操作步骤如下:

第一步:上传一份精心构造的 CSS 到 B。文件内容里写一条 url(),指向 B 那个改状态的接口。

第二步:B 把这份 CSS 当静态文件提供出去,响应头是 text/css

第三步:注意,这里是最妙的一步——攻击者不去找 B 上的注入点,而是直接在自己的恶意页面 A 里加一行:

<link&nbsp;rel="stylesheet"&nbsp;href="https://B.com/file/xxx.css">

第四步:受害者打开 A 页面。浏览器跑去 B 那里拉取这份 CSS 并解析,CSS 里的 url() 随之触发一次发往 B 的请求。

第五步:关键来了。这份 CSS 的”户籍”在 B,所以浏览器给这次请求打的 Referer 是 B 自己的 origin

B 的校验逻辑一看:referer.startsWith('https://B.com/') ✅ 通过。

请求放行,状态被改掉。整条链跑完,攻击者在 B 上连一行 HTML 都没碰过。🤯

图2:CSS 变体利用链——攻击者全程只在自己的页面动手(本文自制示意图)

研究员实测了两个细节,值得记下:

  • Content-Type 卡得很死

    :他专门写了个应用测了两千多种 Content-Type,浏览器肯当作样式表加载的,只有 text/css 这一种。这是这条链仅有的一个硬门槛。

  • 兼容性

    :Chrome 和 Firefox 上都能打,Safari 行为略有出入。

三、变体二:JS 模块,四种 Content-Type 随便挑

第二条链思路一样,只是把样式表换成了 JavaScript 模块。📦

HTML 规范里写得明明白白:一个文档加载了模块脚本,模块又去加载下级脚本时,下级请求的 Referer 用的是模块自己的 URL

和 CSS 完全一个路数:

// 上传到目标站的 JS 文件内容,就这一行import"https://B.com/add-post?content=pwned"

攻击者页面里同样只加一行:

<scripttype="module"src="https://B.com/files/xxx.js"></script>

浏览器拉取模块、执行 import、请求发往 B,Referer 依然是 B 自己的。校验继续形同虚设。

这条链比 CSS 版实用得多,原因就在上图这个对比里👇

图3:CSS 变体只认一种类型,JS 模块变体认四种(本文自制示意图)

text/javascriptapplication/javascriptapplication/ecmascripttext/ecmascript——四种都行。

想想看:一个文件上传功能,返回四种 JS 类型之一的概率,和刚好返回 text/css 的概率,差了可不是一点半点。🎯

写到这里,你可能觉得天塌了。先别慌,把边界说清楚。🧊

这两条链的请求都是跨站发起的,而现代浏览器的 SameSite Cookie 策略默认不带 Cookie。也就是说——

需要登录态才能操作的接口,按上面这套打法是打不动的。

它目前能改掉的,只是那些不需要身份的公开状态。

但研究员也指出了升级路径:如果你能在目标站自己的页面、或者它的子域上完成这个 <link> / <script> 的注入,请求就落进了 SameSite 上下文,Cookie 跟着就带上了。JS 变体还有个额外门槛——需要目标返回带反射 origin 的 Access-Control-Allow-Credentials: true,这种 CORS 错配在子域上恰恰很常见。

到这里,一个”只能 prove 概念的 PoC”就变成了带着受害者会话的真实 CSRF。

五、实战 Checklist:测之前先问自己两个问题

给做渗透测试和安全评审的同学,把原文的方法论压成一张可以带走的清单。✅

遇到一个用 Referer 校验防护状态变更接口的站点,先做这件事:

把”用户可控制响应 Content-Type 的文件上传功能”列为绕过原语。

然后用两个问题判断能走多远:

  1. 能不能进入 SameSite 或子域上下文?

    能——影响升级为带会话的真实 CSRF;不能——止步于无 Cookie 的状态篡改

  2. (JS 变体)目标是否返回带反射 origin 的凭证放行头?

    是——带 Cookie 的跨源请求畅通无阻

参考:

https://afine.com/blogs/bypassing-referer-based-csrf-with-strict-origin-when-cross-origin

END

公众号内容都来自国外等平台- 搜索的内容通过结合编写 –

 提供整洁 – 广告已关

公众号 | AnQuan7 (Ots安全)


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Ots安全 《一个 CSS 文件,掀翻 Referer 防线》

评论:0   参与:  0