人人都会AI编程

24.1 XSS 跨站脚本攻击风险与防护方案

更新时间:2026-07-11

在 Electron 应用中,跨站脚本攻击(XSS)是导致远程代码执行、本地文件泄露和用户数据失窃的最主要入口之一。浏览器的同源策略在 Electron 中默认情况下被大幅放宽,节点集成、上下文隔离关闭等危险配置会直接让一个简单的反射型 XSS 升级为恶意代码在用户系统上以应用权限任意执行的严重漏洞。

24.1.1 为什么 Electron 中的 XSS 比 Web 端更危险

在传统 Web 应用中,XSS 攻击通常只能窃取 Cookie、劫持会话、或篡改页面内容。但由于 Electron 允许渲染进程调用 Node.js 和系统 API,一旦攻击者成功注入脚本,在配置不当的情况下可以做到:

  • 任意文件读写:通过 require('fs') 读取用户敏感文件,或将自己植入的恶意脚本写入自启动目录。
  • 执行系统命令:借助 require('child_process').exec 运行任何命令行操作。
  • 全面控制应用:从修改窗口菜单,到注册全局快捷键、截屏、启动本地服务,所有应用具备的权限都能被利用。
  • 窃取本地数据:读取应用存储的 token、密钥、配置文件,并将其上传到远程服务器。

换句话说,Electron 环境下的 XSS 不再是“网页被篡改”的问题,而是“桌面失守”的现实。

24.1.2 真正的危险在于“默认不安全”的配置

许多团队为了快速上线或沿用老项目模式,会不自觉地使用以下高危配置:

  • 渲染进程直接开启 nodeIntegration: true,且未使用任何上下文隔离。
  • 使用 webview 标签或 BrowserView 载入不可信站点。
  • 在应用中直接展示远程内容(如广告、第三方聊天窗口)而未做沙箱隔离。
  • 不当使用 evalinnerHTMLdocument.write 在前端拼接用户输入。

最典型的攻击场景是:应用内有一个搜索框,输入内容被拼接成 DOM 片段直接插入页面。攻击者构造一个输入值,其中包含隐藏的 <img> 标签加载远程图片,或者通过 <script> 标签加载远程 JS 文件。如果渲染进程拥有 Node.js 能力,攻击者只需 require('child_process').exec('calc') 就能证实命令执行。对于有全盘读写权限的应用,这足以完成从侦察到持久化的完整攻击链。

24.1.3 有效且务实的防护方案

防护不是一两个 API 的开关,而是架构设计、代码规范和运行时策略的共同结果。

(1)开启上下文隔离并禁用 Node 集成

这是最基础的防线。从 Electron 12 开始,推荐配置是:

const mainWindow = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,  // 渲染进程无法直接访问 Electron 或 Node.js 的全局对象
    nodeIntegration: false,  // 渲染进程中没有 require、process、Buffer 等
    sandbox: true,           // 配合启用操作系统的沙箱
    preload: path.join(__dirname, 'preload.js'),
  }
});

开启 contextIsolation: true 后,预加载脚本和渲染器窗口各自拥有独立的 JavaScript 上下文,即使渲染进程被注入脚本,攻击者也无法触及预加载脚本中暴露的 API,因为它们是隔离的。一定要谨慎使用 contextIsolation: false,这个选项在现代开发中几乎不应该出现。

(2)用 contextBridge 安全地暴露接口

预加载脚本中使用 contextBridge.exposeInMainWorld 仅暴露最小权限、声明式的方法给渲染进程,不要暴露完整的 ipcRenderer 对象或 require 功能。

不安全的方式:

// preload.js(坏做法)
const { ipcRenderer } = require('electron');
window.require = require;
window.send = ipcRenderer.send;

安全的方式:

// preload.js(正确做法)
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: () => ipcRenderer.invoke('dialog:openFile'),
  saveFile: (content) => ipcRenderer.invoke('dialog:saveFile', content),
  onMenuAction: (callback) => ipcRenderer.on('menu-action', (_event, value) => callback(value)),
});

这样渲染进程只能调用 window.electronAPI.openFile() 等我们指定的函数,无法直接获得整个 IPC 通道或 Node API。

(3)严格验证与净化所有用户输入

在前端渲染进程中,仍然需要采用经典的 Web 安全措施:

  • 绝不直接拼接 HTML:使用 textContent 替代 innerHTML,或使用经过安全审计的模板引擎自动转义。
  • 使用 DOMpurify 清理富文本:如果必须支持 HTML 渲染,使用成熟的净化库(如 DOMPurify)删除危险的标签和属性。
  • 谨慎使用 evalnew Function:任何动态执行代码的操作都应当避免。
  • 对从主进程传入的数据也要保持不信任:IPC 通道是双向的,即使数据来自主进程,也需要在渲染进程侧做类型检查或过滤。

(4)主进程端增加纵深防御

安全不能只依赖前端的防护。主进程应当:

  • 验证 IPC 调用者的身份:如果使用 ipcMain.on,养成检查 event.senderFrameevent.sender 的习惯,但更好的做法是通过 ipcMain.handle 提供调用式接口,并结合预加载脚本的约束。
  • 敏感操作进行二次确认:例如删除用户数据、执行外部命令等,主进程应弹出系统确认对话框,或要求用户输入密码。
  • 仅打开受控的外部链接:阻止应用打开未知域的 URL,或通过 shell.openExternal 传递的链接必须经过协议和域名的白名单过滤。
  • 限制渲染进程加载内容:如果应用不需要加载远程资源,在 BrowserWindow 中设置 allowDisplayingInsecureContent: false,并通过内容安全策略(CSP)进一步收缩。

(5)配置内容安全策略

在 HTML 文件中添加 CSP 响应头或 <meta> 标签,是限制脚本执行来源的最后一道防线:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';">

这能阻止大多数通过注入 <script> 或远程脚本加载发起的攻击。在 Electron 里还可以通过主进程的会话 API 统一设置 CSP:

session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  callback({
    responseHeaders: {
      ...details.responseHeaders,
      'Content-Security-Policy': ["default-src 'self'"]
    }
  });
});

24.1.4 真实案例的教训

过去几年间,已有数起知名 Electron 应用因 XSS 漏洞导致严重后果的事件。例如,某款即时通讯工具因在聊天消息中直接渲染未过滤的 HTML,攻击者仅需发送一条特殊构造的消息,即可在接收方客户端中执行任意 Node.js 代码,最终实现全盘任意文件读取和持久化后门。事后复盘发现,问题核心就是 nodeIntegration: true + contextIsolation: false 的组合。只要攻击者能够在渲染进程中执行任意 JavaScript,整个客户端就如同裸奔。

修复方案也正是上述几条原则的组合:开启上下文隔离、使用 contextBridge 暴露最小接口、对所有外部内容做 DOMPurify 净化。

24.1.5 检查清单

在日常开发中,可以用以下清单进行自检:

  • [ ] nodeIntegration 是否为 false
  • [ ] contextIsolation 是否为 true
  • [ ] 是否使用了 contextBridge 隔离 API,而未暴露 require 或完整 ipcRenderer
  • [ ] 所有用户可控的输入是否经过转义或净化?
  • [ ] 渲染进程中是否禁止 eval()new Function()
  • [ ] 是否配置了内容安全策略(CSP)?
  • [ ] 主进程中关键操作(文件系统、shell、网络请求)是否做了输入校验或白名单控制?
  • [ ] 第三方库和插件是否经过安全审计,且保持更新?

XSS 攻击在 Electron 中的危害远高于浏览器环境,但相应的防护手段也已经是成熟且标准化的。只要在项目开始时就遵循安全架构原则,就能将这类风险控制在非常低的水平。