Next.js 是基于 React 的全栈 Web 框架,常用于构建服务端渲染、静态生成、API 路由和 Server Actions 混合的现代 Web 应用。它的优势在于开发体验和渲染性能,但一旦服务端请求处理链出现反序列化、原型链污染或非预期函数调用问题,影响就不再只是前端页面异常,而可能变成服务端远程代码执行,RCE(Remote Code Execution,远程代码执行)就是这类风险里最严重的一种。
这类问题的危险点在于:攻击入口看起来像普通 HTTP 请求,真正的问题发生在服务端解析 multipart 表单、处理 Server Actions 标识、恢复 React Server Components 数据结构的过程中。如果业务系统暴露在公网,又没有及时升级依赖或限制异常请求,攻击者可能通过构造请求让服务端执行系统命令、读取环境变量,甚至植入长期驻留的内存型后门。
安全处理这类漏洞时,不应该只盯着某一个 Payload,而要弄清楚三件事:
- 哪些系统可能受影响;
- 请求为什么能进入危险代码路径;
- 如何检测、阻断、升级和验证修复。
影响面识别
从技术栈看,风险主要集中在使用 Next.js、React Server Components、Server Actions 的应用上。实际排查时,不能只看页面是否使用 React,也要确认服务端依赖版本、构建产物和部署框架。
常见指纹包括:
header="Next.js" || body="/_next/static"
如果系统是基于 Dify 这类 Web 应用平台部署,也需要额外检查其前端服务是否使用了受影响的 Next.js / React 组合。
app="Dify"
需要重点检查的资产类型包括:
| 资产类型 | 风险原因 | 排查重点 |
|---|---|---|
| 公网 Next.js 应用 | 攻击者可以直接发送构造请求 | HTTP 响应头、/_next/static、部署版本 |
| 使用 Server Actions 的应用 | Server Actions 会处理特殊请求头和表单结构 | Next-Action 请求头、multipart 表单 |
| 基于 Dify 的平台 | 某些部署可能间接受 Next.js 依赖影响 | 容器镜像版本、前端服务版本 |
| 自托管 Node.js 服务 | 运行权限过高时影响会扩大 | Node.js 进程权限、环境变量、网络访问权限 |
React 版本需要结合项目锁文件确认,例如:
cat package.json
cat package-lock.json
cat pnpm-lock.yaml
cat yarn.lock
可以重点搜索:
grep -E '"react"|"react-dom"|"next"' package.json
grep -E 'react@|react-dom@|next@' pnpm-lock.yaml 2>/dev/null
grep -E '"react"|"react-dom"|"next"' package-lock.json 2>/dev/null
如果是容器化部署,还要进入镜像或运行中的容器检查实际安装版本,因为源码仓库里的版本不一定等于线上版本。
docker ps
docker exec -it <container_name> sh
node -p "require('next/package.json').version"
node -p "require('react/package.json').version"
node -p "require('react-dom/package.json').version"
风险入口:Server Actions 请求是怎样进入服务端的
Next.js Server Actions 允许前端直接调用服务端函数。正常情况下,框架会通过特定请求头、表单字段和内部协议把用户操作映射到服务端 action。这个机制本身是为了减少手写接口代码,但也意味着服务端要解析来自客户端的结构化数据。
一个典型 Server Actions 请求大致会经过这样的路径:
flowchart TD
A[浏览器或客户端发送请求] --> B[Next.js 服务端接收 HTTP 请求]
B --> C{是否包含 Server Actions 特征}
C -- 否 --> D[按普通页面或 API 请求处理]
C -- 是 --> E[解析请求头与 multipart/form-data]
E --> F[恢复 React Server Components 数据结构]
F --> G[定位并执行对应 Server Action]
G --> H[返回渲染结果或重定向响应]
危险发生在 E -> F -> G 这段链路里。如果框架在恢复结构化数据时,对某些特殊字段、原型链属性或函数引用处理不当,恶意输入就可能影响服务端对象行为,使代码执行路径偏离原本设计。
从防守角度看,异常请求通常具备这些特征:
| 特征 | 说明 |
|---|---|
Next-Action 请求头 | Server Actions 相关请求常见特征 |
multipart/form-data | 请求体包含多个表单字段 |
| 非正常字段名 | 表单字段可能使用数字或框架内部结构 |
| 原型链相关字符串 | 例如 __proto__、constructor 等危险关键字 |
| 异常重定向头 | 响应中可能出现携带结果的跳转信息 |
| 命令执行痕迹 | 请求参数、响应头、日志中出现系统命令输出 |
不要把检测逻辑写成“只匹配某一个固定 Payload”。攻击者可以改边界字符串、字段顺序、请求路径、命令内容和回显方式。更可靠的做法是基于协议特征、危险字段、异常响应和进程行为组合判断。
攻击链的核心逻辑
远程代码执行链通常不是“访问一个 URL 就执行命令”这么简单,而是利用框架内部数据恢复逻辑,把恶意结构伪装成框架能够处理的对象。
可以把过程抽象成四步:
sequenceDiagram
participant C as 攻击客户端
participant N as Next.js 服务端
participant R as React Server Components 解析逻辑
participant S as Node.js 运行时
C->>N: 发送带 Server Actions 特征的构造请求
N->>R: 解析 multipart 表单和内部字段
R->>R: 恢复对象关系与异步状态
R->>S: 触发非预期代码路径
S-->>N: 返回执行结果或异常响应
N-->>C: 响应头、响应体或重定向中出现异常信息
其中最关键的是两类能力:
1. 回显型执行
回显型执行会把命令结果通过响应体、响应头或重定向参数带回来。防守方在日志中可能看到一些很不自然的响应,例如:
- 响应状态码异常;
- 重定向目标里包含编码后的长字符串;
- 响应头出现异常字段;
- Node.js 日志里出现 shell 命令调用;
- Web 访问日志里出现
whoami、id、uname、cat /etc/passwd等探测命令痕迹。
这类行为的检测重点不是命令是否成功,而是“Web 请求为什么会让 Node.js 进程调用系统命令”。
2. 内存型驻留
内存型后门不会写入磁盘文件,而是通过修改运行中 Node.js 进程的行为,把某个隐藏路径、特殊参数或事件处理函数变成命令入口。它的特点是:
| 特点 | 说明 |
|---|---|
| 不一定落盘 | 常规文件完整性检查可能发现不了 |
| 重启后消失 | 如果只是内存修改,进程重启会清除 |
| 隐蔽路径触发 | 攻击入口可能伪装成普通 URL |
| 依赖进程权限 | Node.js 进程权限越高,破坏面越大 |
| 日志痕迹有限 | 如果访问量不大,可能只留下少量请求记录 |
应急时不能只删除可疑文件,还要重启受影响的 Node.js 进程、轮换密钥,并检查运行时行为是否恢复正常。
日志排查方法
Web 访问日志
优先检索带有 Server Actions 特征的请求:
grep -i "Next-Action" access.log
grep -i "multipart/form-data" access.log
grep -i "_next" access.log
如果日志里记录了请求头,可以继续查找危险关键字:
grep -Ei "__proto__|constructor|child_process|process\.mainModule|execSync|spawn|NEXT_REDIRECT" access.log
Nginx 默认不一定记录所有请求头。如果没有完整头部,需要检查应用日志、网关日志、WAF 日志或 APM(Application Performance Monitoring,应用性能监控)采集内容。
Node.js 应用日志
检查服务端是否出现异常重定向、序列化错误、Server Actions 解析错误:
grep -Ei "NEXT_REDIRECT|Server Action|multipart|formData|prototype|constructor" app.log
grep -Ei "child_process|exec|spawn|command" app.log
如果使用 systemd 管理服务:
journalctl -u <service-name> --since "2026-06-01" | grep -Ei "NEXT_REDIRECT|child_process|exec|constructor|__proto__"
如果使用 Docker:
docker logs <container_name> 2>&1 | grep -Ei "NEXT_REDIRECT|child_process|exec|constructor|__proto__"
进程行为排查
Node.js 进程不应该频繁创建 shell 子进程。可以用下面的命令查看是否存在异常子进程:
ps -ef | grep -E "node|sh|bash|curl|wget|nc|python|perl"
pstree -ap | grep node
检查监听端口:
ss -lntup
lsof -i -P -n | grep LISTEN
如果发现 Node.js 进程派生了异常命令,或者存在未知监听端口,需要把主机当作已失陷处理。
快速加固措施
1. 升级 Next.js、React 和相关依赖
修复这类框架漏洞,最可靠的方式是升级到官方修复版本。升级前先确认当前版本:
npm ls next react react-dom
使用 pnpm 的项目:
pnpm list next react react-dom
升级依赖:
npm install next@latest react@latest react-dom@latest
或:
pnpm add next@latest react@latest react-dom@latest
升级后重新构建并发布:
npm run build
npm run start
容器化部署需要重新构建镜像,不能只改源码仓库:
docker build -t my-next-app:patched .
docker run --rm my-next-app:patched node -p "require('next/package.json').version"
2. 临时限制异常 Server Actions 请求
在无法立刻升级的情况下,可以在网关层做临时拦截。注意,规则要经过业务验证,避免误伤真实 Server Actions。
Nginx 可以拦截包含高风险关键字的 Server Actions 请求:
map $http_next_action $has_next_action {
default 1;
"" 0;
}
map $request_body $has_dangerous_rsc_payload {
default 0;
~*(__proto__|constructor|child_process|process\.mainModule|execSync|spawn) 1;
}
server {
location / {
if ($has_next_action$has_dangerous_rsc_payload = "11") {
return 403;
}
proxy_pass http://nextjs_backend;
}
}
如果 Nginx 没有读取请求体的配置,这种规则可能无法生效。更稳妥的做法是在 WAF 或应用网关里配置请求体检测。
3. 收紧 Node.js 进程权限
Next.js 服务不应该使用 root 权限运行。可以创建低权限用户:
useradd -r -s /usr/sbin/nologin nextjs
chown -R nextjs:nextjs /opt/my-next-app
systemd 示例:
[Service]
User=nextjs
Group=nextjs
WorkingDirectory=/opt/my-next-app
ExecStart=/usr/bin/npm run start
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
容器部署时也要避免 root 用户:
USER node
同时限制容器能力:
docker run \
--cap-drop=ALL \
--read-only \
--tmpfs /tmp \
my-next-app:patched
4. 保护环境变量和密钥
RCE 的常见后果之一是环境变量泄露。攻击者一旦拿到数据库密码、对象存储密钥、JWT 密钥或第三方 API Token,就可能继续横向移动。
排查和处置时需要轮换:
| 密钥类型 | 处理方式 |
|---|---|
| 数据库账号密码 | 修改密码,检查异常连接和新增账号 |
| Redis 密码 | 轮换密码,检查是否允许公网访问 |
| 对象存储密钥 | 禁用旧 Key,创建最小权限新 Key |
| JWT Secret | 轮换后让旧 Token 失效 |
| OAuth / API Token | 到对应平台撤销并重新生成 |
| CI/CD 凭据 | 检查流水线日志和部署权限 |
如果系统已经出现命令执行痕迹,只升级依赖是不够的,必须按入侵事件处理。
Dify 部署环境的检查重点
Dify 常见部署方式是 Docker Compose。排查时可以查看所有服务镜像和版本:
docker compose ps
docker compose images
进入 Web 或前端相关容器检查依赖:
docker compose exec <web_service_name> sh
node -p "require('next/package.json').version" 2>/dev/null
node -p "require('react/package.json').version" 2>/dev/null
node -p "require('react-dom/package.json').version" 2>/dev/null
如果镜像内没有 Node.js 依赖目录,说明构建产物可能已经被打包,需要根据镜像标签、发布说明和官方安全公告确认是否包含修复。
Dify 环境还要额外关注这些内容:
| 检查项 | 原因 |
|---|---|
.env 文件 | 里面通常包含数据库、Redis、对象存储和模型服务密钥 |
| PostgreSQL 数据库 | 需要检查异常账号、异常连接和敏感数据访问 |
| Redis | 如果被滥用,可能成为任务队列或会话数据入口 |
| 插件和模型服务 Key | 泄露后可能造成额外费用或数据暴露 |
| 容器网络 | 失陷 Web 容器可能访问内部服务 |
入侵判断清单
出现以下任意情况,都应该按高风险事件处理:
- Web 日志中出现大量带
Next-Action的异常 multipart 请求; - 请求体或日志里出现
__proto__、constructor、child_process、execSync等关键字; - 响应头或重定向地址里出现疑似命令结果的编码内容;
- Node.js 进程派生了
sh、bash、curl、wget、nc等异常进程; - 服务进程内出现未知路由行为,重启后消失;
- 环境变量、配置文件、数据库凭据存在被读取迹象;
- 容器里出现未知文件、计划任务或新增用户;
- 出站连接访问陌生 IP、矿池、反连平台或临时文件下载站。
应急流程可以按下面顺序执行:
flowchart TD
A[发现异常请求或告警] --> B[保留日志和现场信息]
B --> C[隔离受影响实例]
C --> D[检查依赖版本和进程行为]
D --> E{是否存在执行痕迹}
E -- 否 --> F[升级依赖并增加拦截规则]
E -- 是 --> G[按失陷主机处理]
G --> H[轮换密钥和检查横向移动]
F --> I[重新构建发布]
H --> I
I --> J[复测与持续监控]
修复后的验证方式
修复不是“升级命令执行完”就结束,需要验证线上服务实际运行的是新版本。
检查构建产物:
node -p "require('next/package.json').version"
node -p "require('react/package.json').version"
node -p "require('react-dom/package.json').version"
检查服务是否重新启动:
ps -eo pid,lstart,cmd | grep node
检查容器镜像创建时间:
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.CreatedAt}}\t{{.Status}}"
检查近期是否仍有异常请求:
grep -Ei "Next-Action|__proto__|constructor|child_process|execSync|NEXT_REDIRECT" access.log
如果有 WAF 或网关,可以添加告警规则,而不只是静默拦截。静默拦截只能减少攻击成功率,告警才能帮助判断是否有人正在针对资产批量扫描。
长期防护建议
Next.js 这类全栈框架把前端交互、服务端渲染和接口调用整合在一起,安全边界比传统静态前端更复杂。长期防护应覆盖依赖、运行时、网关和监控四层。
| 层面 | 建议 |
|---|---|
| 依赖管理 | 使用 lockfile,定期运行 npm audit、Dependabot 或 Renovate |
| 构建发布 | 每次升级后重新构建镜像,禁止线上手工改包 |
| 网关防护 | 对 Server Actions 异常请求、危险关键字和异常 multipart 做检测 |
| 运行权限 | Node.js 进程使用低权限用户,容器去除多余 capability |
| 密钥管理 | 环境变量最小化,密钥定期轮换,避免把高权限 Key 放进 Web 容器 |
| 日志监控 | 记录请求头、异常响应、进程派生和出站连接 |
| 应急预案 | 明确隔离、取证、升级、轮换密钥和恢复流程 |
RCE 类漏洞的处置重点是“尽快阻断入口,确认是否已经执行代码,再决定是否需要按失陷处理”。如果只修版本、不查日志、不轮换密钥,已经泄露的凭据仍然可能被继续使用。对于暴露在公网的 Next.js 和 Dify 部署,升级依赖、限制异常 Server Actions 请求、降低 Node.js 运行权限,应当同时完成。