关于 nextjs 的新漏洞的一些碎碎念

Webnextjs
浏览数 - 220发布于 - 2025-12-11 - 23:32
GSGFs
GSGFs

231

前几天, 最受欢迎的前端框架 React, 又爆出了一个震惊整个前端界的漏洞, CVE-2025-55182 这个漏洞的得分为 10/10, 满分. 可以说是前端历史上最严重的漏洞了 (毕竟最高只有 10 分)

那么 React 的这个漏洞跟标题里写的 Next.js 又有什么关系呢, 只要升级一下 React 就可以修复这个漏洞了.

其实并非如此, 其实 React 这个包里面 "什么都没有", 这个包里面其实都是一些 React 的逻辑和类型定义, 比如说 React 的组件的模型, 虚拟 DOM 的运行逻辑, 各种 HOOK 等. 因为 React 其实是有两种渲染器的, 一个是运行在浏览器上的 react-dom, 一个是开发手机端应用程序的 react-native, 而 react 这个包需要同时兼顾他们两个.

但是这次出锅的都不是上面提到的任意一个包, 而是 react-server-dom-* 这些包. (比如说 react-server-dom-webpack)

这些包是用于实现 Flight 协议的, 这个协议用于 React 组件客户端与服务端之间的交互 (比如说 Next.js 的 server action, 这个很好用的功能背后就是依靠 Flight 协议实现的.)

但是这些包都是 Next.js 的底层依赖, 所以你在 package.json 中找不到这些依赖, 并且就算你自己装了个新的 Next.js 也会坚持使用自己的旧版本. 所以想要修复这个问题也只能升级 Next.js 了.

这也就是 Next.js 提交 CVE-2025-66478 这个漏洞的原因了, 毕竟 "原生" React 又不依赖这个包, 给自己标上这个漏洞可以方便快速排查问题, 虽然这个 CVE 被拒了 (因为与 React 的重复了). 这个漏洞在 GitHub 上的编号为 GHSA-9qr9-h5gf-34mp.

其实吧, 这个漏洞最根本的原因是 React 导致的, 但是开发 React 和 Next.js 都是同一群人 (Vercel), 所以其实也没有错怪.


那么究竟为什么 React19 之后这个漏洞就显现出来了呢, React18 却丝毫不受影响. 其实还是主要因为, Vercel 大推 SSR(Server-Side Rendering, 服务端渲染), React19 就引入了很多服务端功能, React 的 Flight 协议作为客户端与服务端交互的基石, 自然也迎来了大量的更新.

Flight 在这次的更新中也支持了 "循环引用", Promise, Map, Date 等高级特性. React 这次的漏洞就是因为这些新的高级功能没有良好的安全防护, 在 Flight 协议反序列化的过程中甚至可以通过注入 __proto__ 等属性污染 JS 对象的原型链, 可以使用这种方式来修改对象的各种属性, 甚至注入自己的代码.

比如说如果注入了下面的这段代码, 就可以快速 "更新"(换新?) 系统.

js
const { exec } = require("sub_process");
exec("sudo rm -rf /*");

代码跑不起来? 因为我故意写错了. 怕你真的会去试.

所以我推荐容器化部署喵, 大幅减低被渗透的风险. (虽然也可能被当成肉鸡)

那么 JS 的原型链又是什么呢, JS 的对象与其他一些语言不太一样, 一些语言在使用类型继承的时候会创造一个新的类, 并把父类的属性全部复制过来. 但是 JS 有点不太一样, 他是使用一种叫作 "原型链" 的方式继承对象的.

在 "原型链" 中, 你从某个类中派生出一个新类之后, 这个新的类并不会直接复制父类的所有属性, 而是将自己的 __proto__ 属性指向自己的 "原型"(父类), 如果我需要使用这个对象的某个方法, 但是在这个新的类中是我没有定义的, 那么就会接着顺着 __proto__ 找父类, 看看他是否提供了这个方法, 以此类推.

那么原型链污染呢, 也是专门针对 JS 原型链的攻击, 因为他是动态类型的语言, 可以随意的设置对象的属性, 比如说在早期的 JS 中, 大伙都是这样使用 "类" 的:

js
function MyClass() {
  // 保护机制, 防止你忘记 new 出一个对象
  if (!(this instanceof MyClass)) {
    return new MyClass();
  }

  this.message = "Ciallo~(∠・ω< )⌒★";
}

MyClass.prototype.greet = function () {
  console.log(this.message);
};

new MyClass().greet();

因为 class 关键字在 ES6 之后才出现, 并且 class 其实也是针对原型链机制的语法糖, 想让上面的代码更加现代化的话, 也可以写成下面这样:

js
class MyClass {
  constructor() {
    this.message = "Ciallo~(∠・ω< )⌒★";
  }

  greet() {
    console.log(this.message);
  }
}

new MyClass().greet();

看完了上面的例子, 那再来看看怎么用 __proto__ 来实现注入

js
// ...

// 直接修改 Object.prototype 的属性
MyClass.prototype.__proto__.greet = function () {
  console.log("★⌒( <ω・∠)~ollaiC");
};

// 随便创建一个对象
const obj = {};

// 输出: ★⌒( <ω・∠)~ollaiC
// 反 ciallo 病毒植入成功
obj.greet();

但是针对 React 的攻击会更加复杂, 他利用了 Flight 解析器的漏洞, 没有针对特定的内容进行审查, 导致原型链污染, 再加上这个解析器本来就可以解析函数, 这样的一套组合就导致了攻击者可以直接在你的服务器上执行任意的代码.

至于具体攻击我就不演示了, 总之绝对不是我不会 (毕竟咱也只是一个业余的代码爱好者).

其实这些在动态类型语言里都很常见, 比如说在 Python 里, 你可以给任意一个对象加入 __call__ 属性, 让他变成一个 "函数":

python
class MyClass:
    pass


cls = MyClass()
# cls()  # 报错: TypeError: 'MyClass' object is not callable


def call(*_, **__):
    print("ciallo")


MyClass.__call__ = call
cls()  # 输出: ciallo

因为 Python 中的 __call__ 是 "魔术方法", 会绕开实例, 直接查找类是否有这个方法. 比如说你调用 obj() 实际上执行的是 type(obj).__call__(obj). 这貌似与 Python 的一个设计原则有关, 「类定义行为, 实例继承状态」.

补充一点, 这并不代表哪个语言更好, 更优秀, 这只是在不同的设计理念的驱动下所诞生的产物, Python 主张简单清晰, 所以某些功能会被分离, 而 JS 在设计之初就是为浏览器的环境量身打造的, 用于在浏览器里进行简单操作, 这些属性可以被修改, 也是为了适应浏览器重复且复杂的环境. 就像是 Linux 的发行版, 你能说哪个发行版就一定是最好的吗, 有的只是更适合自己的罢了. (这些无意义的骂战不知道持续多少年了, 已经看腻了喵, 所以请不要再重复了. 不过前阵子某个写入教科书级的人物声称要用 Fedora 统治世界)

Thank you NVIDIA

说起 Python, 前些阵子 Python 的 Django 框架也爆出了一个漏洞 (具体见 这篇文章), 不过也只是潜在的漏洞, 没有 CVE 的危险评分. 其中一个漏洞是关于 SQL 注入的, 因为 Django 没有正确的过滤字典的键名, 导致在给 Django ORM 使用 **kwargs 这样的方法传递参数的时候会导致 SQL 注入.

(内心 OS: 这么有人这样用数据库啊, 直接把用户的输入作为 Django ORM 的参数, 嫌自己的数据库不会自己爆炸嘛)

毕竟后端的第一安全原则就是不要相信用户任何的输入, 但是如此 Django 对这个漏洞的安全风险评级还是为 "高".

扯远了, 说回 Next.js, 我一直觉得 Next.js 有一个非常大的优势, 他的前后端开发风格的一致性相当好, 在前端也是写 TSX, 在后端也是写 TSX, 开发体验非常流畅. 我之前学过一点点 React, 后面拿着 Next.js 基本上随便看看文档就会写了. 然而我之前写我的博客的时候就折腾的不行, 一边是 js 一边是 py, 两边换来换去写的我都快脑裂了, 并且 py 的风格化还相当的强 (缩进语法).

python 用户 belike

但是一致性高又有什么问题呢? 那就是分不清前后端, 某些应该在服务端的代码, 后时候写糊涂了直接就导入到客户端里了, 尝试在客户端调用这些功能. 普通的服务端函数倒是没什么, 因为他们会变成 server action, 如果你忘记标注 "use server" 呢? 那么你的整个函数可能就会被打包进前端, 如果你的函数里又调用了外部的什么服务, 那么连着你的 api key 也会被一起泄露. (我 hack 了我自己?)

包括这次的漏洞也是的, 他们明显没有搞清楚自己在写后端, 拿到数据就用, 无语, 跟你说不下去, 典型的前端思维.png

其实 Next.js 的 server action 也是的, 他的本质也是 RPC (Remote Procedure Call), 就算在客户端上通过了验证, 在服务端的 server action 里也要再验证一遍.

其实 Next,js 还有专门的 DAL (Data Access Layer, 数据获取层) 来标注某些代码只能在服务端运行, 如果被导入到客户端的话就会触发编译时错误. 按照最佳实践来说, 所有的敏感操作都应该丢到 DAL 里, 比如说数据库的相关操作等. 并确保所有的敏感信息不会被泄露到服务端组件或是 server action 的返回值中.

tsx
import "server-only"; // 定义 DAL

// ...

export async function getUsers() {
  const session = await verifySession();
  if (!session) {
    // 确保当前用户有权访问这些数据
    return null;
  }

  try {
    const data = await db.query.user.findMany({
      // ...
    });

    return data;
  } catch {
    // ...
  }
}

并且, 有了 DAL 之后就不需要 "use server" 里烦人的异步操作了, 可以直接导出某个可以操作的对象, 直接供其他的地方调用, 避免其他地方的逻辑全部被聚集到这一块. 比如说要白嫖 cf 的 AI 的话可以这样写:

tsx
import "server-only";

import { OpenAI } from "openai";

export const ai = new OpenAI({
  apiKey: process.env.CLOUDFLARE_API_KEY,
  baseURL: `https://api.cloudflare.com/client/v4/accounts/${process.env.CLOUDFLARE_ACCOUNT_ID}/ai/v1`,
});

然后就可以在其他的组件里调用这个 ai 对象了, 不用全挤在一块.

为什么 "use server" 需要所有的函数都是是异步的呢, 因为这可能是一个 server action, 而 server action 的本质也是一次网络请求, 加上异步可以保证前后端的一致性.

说到 cf (Cloudflare), 他为了蹭这个漏洞的热度, 宣布自己的 WAF 可以抵御这个攻击, 不论是否付费都可以享受安全保护, 然后... 半个互联网又宕机半个小时.

(咳咳, 其实只有大概 28% 的流量受影响, 大部分还是正常的)


最后, 论坛的这个实时编辑器好用喵~

Sticker

本文版权遵循 CC BY-NC 协议 本站版权政策

7 条回复

bfloat16
发布于 2025-12-12 - 06:25

vercel直接把react代码复制了一份塞nextjs里面去了,连submodule都不用,纯纯的逆天。

这构思公司只想着给自己的云服务引流,然后狂推SSR,完全不吸取php的教训,设计的纯一坨…

我坐等nextjs后面继续爆CVE漏洞(

鲲

7094

#2
发布于 2025-12-12 - 08:47

某三角形公司刚发给我的新漏洞

image.png

kotolizili
发布于 2025-12-12 - 12:07 (编辑于 2025-12-12 - 12:08)

世界就是一个巨大的草台班子喵。

传说中,一段代码,其中8%是功能代码,其余的90%是为了保护维持那8%的功能,剩下的那2%,则是不明意义,不知所谓,莫名其妙的代码,但是拿掉就会炸程序的代码,贡献了85%的漏洞。

这段话是猫猫瞎编的喵。。。

但事实说不定真是这样呢喵。。。Sticker

GSGFs
发布于 2025-12-12 - 14:45
回复 @bfloat16#1

vercel直接把react代码复制了一份塞nextjs里面去了,连submodule都不用,纯纯的逆天。 这构思公司只想着给自己的云服务引流,然后狂推SSR,完全不吸取php的教训,设计的纯一坨… 我坐等nextjs后面继续爆CVE漏洞(

Sticker

难怪我在我的 pnpm-lock.yaml 里都找不到 react-server-dom-webpack 这个包

GSGFs
发布于 2025-12-12 - 14:51
回复 @鲲#2

某三角形公司刚发给我的新漏洞 !image.png

Sticker

今年第三次了,还能相信 Vercel 吗

GSGFs
发布于 2025-12-12 - 15:41

原来图片被拦了吗,为什么我自己的浏览器可以加载Sticker

GSGFs-1765525249346-Screenshot 20251212 at 073922  nextjs    Galgame    Galgame png

kotolizili
发布于 2025-12-13 - 17:17
回复 @GSGFs#6

原来图片被拦了吗,为什么我自己的浏览器可以加载!Sticker !GSGFs-1765525249346-Screenshot 20251212 at 073922 nextjs Galgame Galgame png

So, NVIDIA fxxk you喵

(。>︿<。) 已经一滴回复都不剩了哦~