本节摘要:RCE 是 Electron 应用可能遭遇的最严重攻击——攻击者从"在页面里执行脚本"一路走到"在用户系统上执行命令"。本节拆解一条完整攻击链的三个跳板(输入污染、脚本注入、能力滥用),逐一给出阻断手段,重点讲命令拼接漏洞与转义失效的机理,并以一个有奖漏洞报告的修复过程为例演示全链路防御。
先给结论式的心智模型:一次成功的 RCE 需要连续跳过三个跳板——输入污染(不可信数据进入应用)、脚本注入(污染数据获得脚本执行权)、能力滥用(脚本调用到不受限的系统能力)。防御的全部要义就是让这条链在任何一环断掉,而且最好不止一环设防(纵深防御)。前两节已覆盖"能力层"(基线开关)与"来源层"(CSP),本节补齐最贴近代码的三件事:输入怎么变成注入、注入怎么够到能力、以及最常见的实操漏洞形态。

Electron 页面的 XSS 与普通网页同理,最高频的入口是 innerHTML。对比两种写法的机理:
// 危险:把用户数据当 HTML 解析,载荷直接成为脚本上下文的一部分 list.innerHTML = items.map(i => `<li>${i.title}</li>` // i.title 若含恶意标签与属性,随之进入 DOM ).join(''); // 安全:数据只当文字,不当标记 for (const i of items) { const li = document.createElement('li'); li.textContent = i.title; // 无论内容是什么,都只是一个文本节点 list.appendChild(li); }
框架用户也不可高枕无忧:React 的 dangerouslySetInnerHTML、Vue 的 v-html,名字里就带着警告。规则可以简化成一句——凡是"把字符串当 HTML"的 API,喂给它的内容里就不允许出现用户数据,除非先经过专门的净化库处理。
Electron 应用常要在主进程里调用外部工具(转码、压缩、调用命令行工具),这里埋着 RCE 的经典形态——把外部输入拼进命令行字符串:
// 漏洞形态:字符串拼接命令 const { exec } = require('node:child_process'); ipcMain.handle('convert', async (_e, inputFile) => { exec(`convert-tool "${inputFile}" --out temp`, (err) => { /* ... */ }); // inputFile 来自页面,页面可能被注入 // 输入 "x" && rm -rf ~ 之类的内容将越过引号获得执行 }); // 修复形态:参数数组,不经过 shell 解释 const { execFile } = require('node:child_process'); ipcMain.handle('convert', async (_e, inputFile) => { if (!/^[A-Za-z0-9_\-./\\:]+$/.test(inputFile)) { throw new Error('文件名含非法字符'); // 第一层:入口白名单校验 } return new Promise((resolve, reject) => { execFile('convert-tool', [inputFile, '--out', 'temp'], // 第二层:参数数组 (err, stdout) => err ? reject(err) : resolve(stdout)); }); });
两层修复各有分工:参数数组让每个参数只可能被当作数据(不存在 shell 元字符解释问题),入口校验则在数据进入主进程前就拦掉异常形态。**两层的防护对象不同,不能用一层替代另一层。**顺带一提,execFile 与 exec 的区别正是"不经 shell 解释",这类"同族 API 安全性不同"的知识点,是主进程代码评审的重点区域。 评审时可以配一条机械化检查:全库搜索子进程调用点与 HTML 拼接点,逐一确认前者用了参数数组形态、后者的输入不含用户数据——两个搜索动作五分钟,能拦住绝大多数高危形态的初级版本。
背景:一款支持"用外部编辑器打开附件"的笔记应用,白帽提交报告:构造一个特殊文件名的附件(内嵌引号与命令分隔符),点击"用编辑器打开"后,用户的系统会静默执行附加命令。漏洞等级评为严重。
操作:定位发现主进程处理器用 exec 拼接了完整命令行,且文件名未做任何校验。修复三件套:换 execFile 参数化调用;文件名白名单校验(不合法直接报错,不尝试修复);preload 暴露的方法收紧参数类型(只收数据库内已有附件的 ID,不收文件名——把"自由文本"降级为"受限引用")。
结果:原攻击路径完全失效。复盘时团队进一步把"所有调用子进程的位置"列为代码评审的强制扫描项,并在自查表里加了对应一行。
解读:第三件修复最有味道——从"传内容"改为"传引用",攻击者就失去了构造空间。ID 只能指向库里已有的附件,任何载荷都无处藏身。变式:凡是"路径、文件名、URL、命令参数"类自由文本输入,都值得问一句能不能换成受限引用;不能换的,校验加参数化双保险。
攻击链拆完,最后一节收尾整个安全工程:权限最小化的分级方法与依赖供应链的审计流程。