agent 需要管理员/root 权限的操作怎么做?提权方案与风险?
考察点
- 跨平台提权方案
- 权限最小化的架构设计(安全意识的体现)
参考答案
1. 核心原则:主进程不整体提权
让 agent 整个以管理员/root 运行是最差方案:
- 渲染进程漏洞直接变成系统级漏洞
- 用户文件全部变成 root 所有,后续普通权限操作各种权限错误
- macOS 下提权应用的 GUI 行为异常(拖拽、剪切板失效)
正确架构:普通权限主进程 + 最小化特权操作,只有真正需要权限的那一步才提权。
2. 各平台提权方案
(1)一次性提权执行(写 hosts、装证书、改系统配置)
// sudo-prompt:跨平台弹系统授权框执行单条命令
const sudo = require('sudo-prompt');
sudo.exec('cp ./app.hosts /etc/hosts', { name: 'My Agent' }, (err) => {
if (err) logger.error('提权执行失败', err);
});
- Windows:触发 UAC 弹窗
- macOS:触发密码输入框(osascript 实现)
- 适合低频操作,每次都会弹窗
(2)常驻特权助手(Privileged Helper)—— 高频操作的正解
架构:普通权限 agent ↔ 本地 IPC(socket/named pipe)↔ root 权限助手进程
- macOS:
SMJobBless安装 launchd 守护(苹果官方方案,electron-builder 有对应配置) - Windows:安装时(安装包本身有管理员权限)注册一个 Windows 服务,以 SYSTEM 运行
- 安装时一次性提权,之后 agent 通过本地 socket 向助手发指令,不再弹窗
// agent 侧:通过 named pipe 请求特权助手
const client = net.connect('\\\\.\\pipe\\my-agent-helper');
client.write(JSON.stringify({ action: 'writeHosts', entries }));
助手的安全设计(重点):
- 只暴露预定义白名单操作,绝不执行任意命令
- 校验调用方身份(如校验对端进程签名/路径)
- 本地 socket 文件权限收紧
(3)安装期完成(最省心) 能放到安装/卸载阶段做的就不放运行时:
- nsis 安装脚本写注册表、创建服务、释放驱动文件(安装包天然有管理员权限)
- macOS pkg 安装包的 preinstall/postinstall 脚本
3. 常见需要权限的操作清单
| 操作 | 推荐方案 |
|---|---|
| 写 hosts / 系统代理设置 | 一次性提权 |
| 安装根证书 | 一次性提权(macOS 用 security 命令) |
| 创建/管理系统服务 | 安装期 + 特权助手 |
| 杀其他进程 / 读取其他进程信息 | 特权助手 |
| 全局键盘/鼠标钩子 | macOS 是辅助功能权限(不是 root),Windows 普通权限即可 |
| 驱动加载 | 安装期,且 macOS 需要 Apple 额外 entitlement |
4. 风险与合规
- 供应链风险:特权助手是攻击高价值目标,二进制必须签名、更新必须验签
- 命令注入:拼接 shell 命令时参数严格转义,最好用 execFile 传数组而不是 exec 拼字符串
- macOS 审核:SMJobBless 方案符合苹果规范;fork bomb 式提权会被拒
面试话术
原则是"最小特权":agent 主体普通权限运行,把特权操作收敛到一个独立的 helper 进程(macOS SMJobBless / Windows 服务),通过本地 socket 通信且只暴露白名单指令。一次性操作用 sudo-prompt 弹 UAC。整个应用绝不整体提权运行——这是安全和稳定性的双重红线。
常见追问
- 怎么判断当前是否已有管理员权限? Windows 可用
net session尝试(失败即非管理员);Node 里也有is-elevated之类的原生模块。macOS 检查process.getuid() === 0。 - 用户拒绝授权怎么办? 功能降级 + 明确提示哪些能力不可用,不能反复弹窗骚扰。