# Manus AI 安全审计报告

> 审计对象：Manus Desktop v1.4.1 (Electron 41.1.1)
> 审计数据：逆向代码（dist/ + frontend/）、HTTP 抓包（1,472 条记录）
> 审计日期：2026-03-31
> 审计人：安全审计 Agent

---

## 审计摘要

| 风险等级 | 发现数量 |
|---------|---------|
| **高** | 4 |
| **中** | 5 |
| **低** | 4 |
| **信息** | 3 |
| **合计** | **16** |

---

## 一、凭证泄露（Credential Exposure）

### [高] F-001: AWS IAM 静态 Access Key 泄露

**证据**：抓包数据中发现 AWS4-HMAC-SHA256 签名使用了静态 IAM Access Key：

```
Credential: AKIAZV3A2ECZCJJZFG7Z/20260404/us-east-1/s3/aws4_request
```

**分析**：
- 前缀 `AKIA` 表明这是 IAM User 的永久 Access Key（非临时凭证 `ASIA`）。
- 该 Key 用于 S3 预签名 URL 生成，授权用户上传文件到 `vida-private` 桶。
- 抓包中同时发现 STS 临时凭证（前缀 `ASIAZV3A2ECZ`）用于 `GetProjectFile`，说明系统混用了两种认证方式。
- IAM 静态 Key 不会自动过期，一旦泄露，攻击者可持续访问 S3 资源。

**可被滥用的方式**：
1. 该 Key 签名的 S3 预签名 URL 有效期为 603,800 秒（约 7 天），在有效期内任何持有 URL 的人均可上传/下载文件。
2. 若 Key 本身泄露（如中间人攻击或日志泄露），攻击者可直接使用 AWS CLI 操作 `vida-private` 和 `vida-prod-gitrepo` 桶中的所有数据。
3. Key 的 IAM 权限范围未知，但至少具备 S3 读写权限。

**风险等级**：**高**
**建议**：全面改用 STS 临时凭证（AssumeRole），淘汰静态 IAM Key。预签名 URL 应在服务端生成后再下发到客户端。

---

### [高] F-002: S3 桶名和 CloudFront Key-Pair-Id 暴露

**证据**：
```
S3 桶：vida-private（用户上传）、vida-prod-gitrepo（WebDev 项目代码）
CloudFront Key-Pair-Id: K2HSFNDJXOU9YS
CDN 域名：private-us-east-1.manuscdn.com
```

**分析**：
- 桶名暴露使攻击者可尝试枚举和探测 S3 桶配置错误（如公开读取、桶策略过松）。
- `vida-private` 的命名暗示包含用户私有数据，一旦桶策略配置不当，所有用户文件均面临泄露风险。
- CloudFront Key-Pair-Id `K2HSFNDJXOU9YS` 是签名 Cookie 的公钥标识。虽然单独暴露不足以伪造签名（还需私钥），但它缩小了攻击面，使攻击者只需获取私钥即可伪造任意 CDN 签名 Cookie。
- `vida-prod-gitrepo` 桶存放用户的 WebDev 项目源码，攻击价值极高。

**风险等级**：**高**（与 F-001 组合后风险倍增）
**建议**：
1. 对 S3 桶启用严格的桶策略和 ACL，确保无公开访问。
2. 定期轮换 CloudFront 密钥对。
3. 在客户端代码中不要硬编码桶名。

---

### [中] F-003: S3 预签名 URL 有效期过长（7 天）

**证据**：上传预签名 URL 的 `X-Amz-Expires=603800`（603,800 秒 = 约 7 天）。

**分析**：
- AWS 官方建议预签名 URL 有效期应尽量短（通常 15 分钟到 1 小时）。
- 7 天有效期意味着一旦 URL 被截获（如日志、浏览器历史、代理缓存），攻击者有 7 天窗口期可上传恶意文件到用户的 S3 路径。
- 下载预签名 URL 有效期为 3,600 秒（1 小时），相对合理。
- 上传和下载的有效期差异表明开发团队有意识地做了区分，但上传的 7 天过于宽松。

**风险等级**：**中**
**建议**：将上传预签名 URL 有效期缩短至 15-30 分钟。如需断点续传，由后端按需生成新 URL。

---

## 二、认证安全（Authentication Security）

### [中] F-004: Token 以明文存储在本地 JSON 文件中

**证据**：`storageHelper.js` 显示 token 存储于 `{userData}/localStorage.json`：

```javascript
// storageHelper.js
this.storagePath = node_path_1.default.join(electron_1.app.getPath('userData'), 'localStorage.json');
// ...
storageHelper.set('session_id', token);
storageHelper.set('token', token);
```

**分析**：
- 认证 token 以纯文本 JSON 格式存储在本地文件系统中，无任何加密或操作系统级凭证保护。
- 攻击者只需读取 `~/Library/Application Support/Manus/localStorage.json`（macOS）即可窃取用户会话。
- 同一文件还存储 `deviceId`、`locale` 等信息，但 token 是最敏感的。
- 相比之下，正确做法是使用 Electron 的 `safeStorage` API 或操作系统 Keychain/Credential Manager。

**风险等级**：**中**
**建议**：使用 `electron.safeStorage.encryptString()` 加密 token，或存储到系统 Keychain（macOS Keychain / Windows Credential Manager）。

---

### [中] F-005: VNC OTP 无明确过期机制

**证据**：抓包中 `requestVncOtp` 返回：

```json
{
  "authEnabled": true,
  "otp": "2cf558dec77e4551ba41e3cb582ab65a",
  "nekoOtp": {
    "username": "neko",
    "webrtc_token": "ec2a0aeb520e9fd7"
  }
}
```

**分析**：
- OTP（一次性密码）用于 VNC/neko 远程桌面认证，但从抓包数据无法确认其过期时间。
- OTP 长度为 32 位十六进制（128 bit 熵），暴力破解不可行。
- `webrtc_token` 仅 16 位十六进制（64 bit），相对较弱。
- 固定用户名 `neko` 减少了一个认证因子。
- 若 OTP 不设过期或过期时间过长，截获后可持续窥视用户的 Agent 桌面操作。

**风险等级**：**中**
**建议**：OTP 应设置短有效期（30-60 秒），且使用后立即失效。`webrtc_token` 应增加至至少 128 bit。

---

### [低] F-006: WebSocket 认证在查询参数中传递 Token

**证据**：`WebsocketHelper.js` 的默认配置：

```javascript
query: {
    token: storageHelper.get('token'),
    locale: storageHelper.get('locale'),
    tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
    clientType: 'desktop',
},
```

**分析**：
- WebSocket 连接通过 URL query 参数传递 token，这意味着 token 会出现在服务器访问日志、代理日志、浏览器历史等处。
- 虽然使用了 WSS（加密传输），但 token 仍可能在日志层面泄露。
- `SocketClient` 对 `myComputer` 功能使用了 `auth` 对象（更安全），但通用 WebSocket 连接仍用 query 方式。

**风险等级**：**低**
**建议**：统一使用 Socket.IO 的 `auth` 选项传递 token，不要放在 URL query 中。代码中 `commonUtils.redactTokenParam()` 表明开发团队已意识到此问题，但治标不治本。

---

## 三、客户端安全（Client-Side Security）

### [高] F-007: Electron 全局禁用 `webSecurity` 和 `sandbox`

**证据**：所有窗口配置（`windowConfig.js`、`setupIPC.js`、`setupNavigationHandlers.js`）统一设置：

```javascript
webPreferences: {
    sandbox: false,
    webSecurity: false,
    nodeIntegration: false,
    contextIsolation: true,
    preload: path.join(__dirname, 'preload.js'),
}
```

**分析**：
- **`webSecurity: false`**：禁用了同源策略（Same-Origin Policy）。这意味着：
  - 渲染进程可以向任意域发起跨域请求，无 CORS 限制。
  - `file://` 协议的页面可以访问任意本地文件。
  - 若渲染进程被 XSS 攻击，攻击者可无限制地与任意后端通信。
- **`sandbox: false`**：禁用了 Chromium 沙箱。渲染进程崩溃或被利用时，攻击范围扩大。
- **正面发现**：`contextIsolation: true` 和 `nodeIntegration: false` 阻止了渲染进程直接访问 Node.js API，这是正确的做法。

**风险等级**：**高**
**建议**：
1. 移除 `webSecurity: false`，这是 Electron 安全最佳实践中最重要的一条。
2. 启用 `sandbox: true`，让渲染进程运行在 Chromium 沙箱中。
3. 如果确实需要跨域请求，应在主进程中通过 IPC 代理。

---

### [高] F-008: `app://` 协议注册了 `bypassCSP` 特权

**证据**：`index.js` 第 26-39 行：

```javascript
protocol.registerSchemesAsPrivileged([{
    scheme: 'app',
    privileges: {
        secure: true,
        standard: true,
        supportFetchAPI: true,
        corsEnabled: true,
        bypassCSP: true,       // <-- 绕过内容安全策略
        allowServiceWorkers: true,
        stream: true,
        codeCache: true,
    },
}]);
```

**分析**：
- `bypassCSP: true` 使得 `app://` 协议下的页面完全绕过 Content Security Policy。
- 这意味着即使前端代码设置了严格的 CSP 头，`app://manus/` 页面下的脚本注入仍然不受限制。
- 结合 `webSecurity: false`（F-007），形成了一个完全无防护的渲染环境。
- `corsEnabled: true` 进一步消除了跨域限制。

**风险等级**：**高**（与 F-007 叠加）
**建议**：移除 `bypassCSP: true` 和 `corsEnabled: true`，改为在前端代码中正确配置 CSP。

---

### [中] F-009: Preload 脚本暴露过度的 IPC API

**证据**：`preload.js` 通过 `contextBridge.exposeInMainWorld` 暴露了以下 API：

| API | 功能 | 风险 |
|-----|------|------|
| `api.send(channel, args)` | 向主进程发送任意 IPC 消息 | 通道名无白名单限制 |
| `api.invoke(channel, args)` | 调用主进程任意 handle | 通道名无白名单限制 |
| `api.readFileAsArrayBuffer(filePath)` | 读取本地任意文件 | **路径无校验** |
| `api.openExternal(url)` | 打开外部 URL | 可打开任意 URL |
| `api.myComputer.addSharedFolder(path)` | 共享本地文件夹给 Agent | 路径有 `validateSharedFolder` 校验 |
| `api.myComputer.updateFolderAllowCmd(path, allowCmd)` | 允许 Agent 在共享目录执行命令 | 可提升 Agent 权限 |
| `localStorage` / `desktopLocalStorage` | 直接替换了 `window.localStorage` | token 等敏感数据可被渲染页面任意读写 |

**关键风险点**：
1. `readFileAsArrayBuffer(filePath)` 没有路径白名单检查，渲染进程可读取任意本地文件（如 `~/.ssh/id_rsa`、`~/.aws/credentials`）。主进程端（`setupIPC.js:149`）仅检查文件是否存在且为文件，未做路径范围限制。
2. `api.send` 和 `api.invoke` 接受任意 channel 名，若主进程新增了敏感 IPC handler，渲染进程可直接调用。

**风险等级**：**中**（因 contextIsolation=true 限制了直接利用途径，需配合 XSS）
**建议**：
1. `readFileAsArrayBuffer` 必须添加路径白名单，限制只能读取用户明确选择的文件。
2. 使用 IPC 通道白名单模式，而非透传任意 channel。

---

### [低] F-010: `open-child-window` IPC 可加载任意 URL

**证据**：`setupIPC.js:48-74`：

```javascript
ipcMain.on('open-child-window', (_, url) => {
    if (typeof url !== 'string') return;
    // ... 创建 BrowserWindow，webSecurity: false, sandbox: false
    childWindow.loadURL(url);
});
```

**分析**：
- `open-child-window` 仅检查 url 是否为 string，未做 URL 白名单或协议校验。
- 攻击者（通过 XSS）可令 Electron 打开任意 URL 到一个具有 `webSecurity: false` + `sandbox: false` + preload 注入的窗口。
- 类似问题存在于 `navigation-back-to-app` 和 `back-to-app-from-injection` handler 中。

**风险等级**：**低**（需先获得渲染进程代码执行权限）
**建议**：对 `loadURL` 的 URL 参数做严格白名单校验，仅允许 `app://manus/` 和已知的内部域名。

---

## 四、数据安全（Data Security）

### [中] F-011: 用户文件隔离依赖 S3 路径前缀

**证据**：用户上传路径为 `s3://vida-private/users/{userId}/uploads/{fileId}_{base64Name}.{ext}`。

**分析**：
- 用户文件隔离完全依赖 S3 路径前缀中的 `{userId}`。
- 如果预签名 URL 生成逻辑存在缺陷（如未校验 userId 与当前会话的匹配），攻击者可能构造路径访问其他用户的文件。
- 从 `FileService/SignUrl` 的调用模式看，签名请求包含完整的 S3 key，需确认服务端是否校验 key 中的 userId 与请求者身份一致。
- `vida-prod-gitrepo` 桶存储用户 WebDev 项目代码，同样依赖路径隔离。

**风险等级**：**中**（需要服务端代码确认，但架构设计存在风险）
**建议**：在服务端强制校验 S3 key 中的 userId 与认证用户身份一致。考虑使用 S3 桶策略或 IAM 条件键进一步限制。

---

### [低] F-012: Sidecar 二进制文件的本地文件系统访问

**证据**：`sidecarManager.js` 显示 sidecar 进程以 `--shared-folder` 参数接收用户共享目录，并通过 WebSocket 连接到沙箱：

```javascript
spawn(binaryPath, ['--shared-folder', sharedFolder, '--ws', wsUrl], {
    detached: process.platform !== 'win32',
    stdio: 'ignore',
    env: { ...process.env, PARENT_PID: String(process.pid) },
});
```

**分析**：
- Sidecar 是一个 Go 编译的二进制文件（未提供源码），负责将用户本地文件夹同步到远程沙箱。
- `updateFolderAllowCmd(folderPath, allowCmd)` 允许用户授权 Agent 在共享目录内执行命令。
- Sidecar 继承了完整的环境变量（`...process.env`），可能包含敏感的 PATH、HOME 等信息。
- 虽然 `validateSharedFolder()` 会检查路径是否为有效目录，但未限制敏感路径（如 `~/.ssh`、`~/`）。

**风险等级**：**低**（需要用户主动共享目录）
**建议**：添加敏感路径黑名单（如 `.ssh`、`.aws`、`.gnupg`），防止用户误操作。Sidecar 进程不应继承全部环境变量。

---

### [低] F-013: 开发环境内部域名泄露

**证据**：`env.js` 包含开发环境 WebSocket 地址：

```javascript
exports.devEnv = {
    chatWebsocketUrl: 'wss://vida.butterfly-effect.dev',
};
```

**分析**：
- `vida.butterfly-effect.dev` 是 Manus 的内部开发环境域名，暴露了内部基础设施信息。
- 产品名 `Vida` 是 Manus 的内部代号（S3 桶名、域名均使用此名称）。
- 虽然该域名在生产构建中不会被使用（`BUILD_ENV='prod'`），但代码中硬编码增加了信息暴露面。

**风险等级**：**低**
**建议**：在生产构建中移除开发环境配置。

---

## 五、OWASP Top 10 检查

### [信息] F-014: A01 - Broken Access Control（访问控制缺陷）

**发现**：
1. **preload API 无访问控制**：`readFileAsArrayBuffer` 可读任意本地文件（详见 F-009）。
2. **IPC 通道无白名单**：`api.send` / `api.invoke` 可发送任意通道消息。
3. **用户文件隔离仅靠路径前缀**（详见 F-011）。
4. **抓包发现 `USER_IS_BLOCKED` 错误**（403），说明存在服务端访问控制，但封禁在抓包进行了一段时间后才触发，风控响应不够及时。

**OWASP 评估**：**存在风险**。客户端侧访问控制薄弱，依赖服务端补救。

---

### [信息] F-015: A02 - Cryptographic Failures（密码学缺陷）

**发现**：
1. **Token 明文存储**：认证 token 以纯 JSON 存储在磁盘上（详见 F-004）。
2. **IAM 静态密钥**：使用永久 Access Key 而非临时凭证（详见 F-001）。
3. **正面**：所有通信使用 TLS/WSS 加密传输。
4. **正面**：AWS S3 预签名使用 AWS4-HMAC-SHA256 签名算法。
5. **正面**：VNC OTP 为 128-bit 随机值，暴力不可行。

**OWASP 评估**：**部分合规**。传输层加密良好，但静态存储和密钥管理存在缺陷。

---

### [信息] F-016: A03 - Injection（注入攻击）

**发现**：
1. **`setupNavigationMonitoring.js` 中的 JavaScript 注入**：使用 `executeJavaScript` 注入 "返回" 按钮，其中 `buttonTitle` 和 `lastMainAppUrl` 变量直接拼接到 JS 字符串中：
   ```javascript
   button.title = '${buttonTitle}';
   // ...
   window.api.navigation.backToApp('${lastMainAppUrl}');
   ```
   - `buttonTitle` 来自固定的翻译表，风险较低。
   - `lastMainAppUrl` 来自导航历史，若被恶意 URL 注入引号可导致 XSS。
2. **协议处理器解析**：`handleProtocolCallback` 直接从 `manus://` URL 中提取 `token` 参数并存储，未做格式校验。
3. **前端 `dangerouslySetInnerHTML`**：在至少 20 个 JS chunk 中发现使用，属于 React 应用常见模式，但需确保输入经过清理。

**OWASP 评估**：**存在风险**。`executeJavaScript` 模板拼接是典型的注入点。

---

### A07 - Authentication Failures（认证缺陷）

已在 F-004、F-005、F-006 中详细分析。核心问题：
- Token 明文存储
- WebSocket token 在 URL query 中传递
- VNC OTP 无明确过期机制

---

## 六、架构级发现（Architecture Findings）

### 6.1 "My Computer" 功能的攻击面

`myComputer` 模块允许用户将本地目录共享给远程 Agent，并可选启用命令执行权限。这引入了一个从云端到本地的攻击路径：

```
远程 Agent (沙箱) → WebSocket → Sidecar 二进制 → 本地文件系统
```

如果沙箱被恶意提示词攻击（prompt injection），Agent 可能：
1. 读取用户共享目录中的敏感文件
2. 在 `allowCmd=true` 时执行任意命令
3. 通过 WebSocket 将数据回传到沙箱

### 6.2 自动更新无签名验证确认

`autoUpdaterManager.js` 使用 `electron-updater` 从 `https://download.manus.im/` 拉取更新。代码中未见 `provider` 配置或代码签名校验逻辑。`electron-updater` 默认会验证更新包签名，但应确认 `download.manus.im` 服务器的安全性和证书固定（certificate pinning）。

### 6.3 Socket.IO 无消息签名

客户端与服务端的 Socket.IO 通信（`device_config`、`device_status`、`device_request`）仅依赖 token 认证，消息本身无签名或 HMAC 校验。中间人攻击（在 TLS 终止后）可篡改配置消息。

---

## 七、风险矩阵

| 编号 | 风险 | 等级 | OWASP | 可利用性 | 影响 |
|------|------|------|-------|---------|------|
| F-001 | AWS IAM 静态 Key 泄露 | **高** | A02 | 需抓包/日志 | 全量 S3 数据泄露 |
| F-002 | S3 桶名 + CloudFront Key 暴露 | **高** | A01 | 已公开 | 扩大攻击面 |
| F-007 | webSecurity=false + sandbox=false | **高** | A01 | 需 XSS 触发 | 本地文件读取 + 任意请求 |
| F-008 | bypassCSP=true | **高** | A03 | 需 XSS 触发 | CSP 防线完全失效 |
| F-003 | 预签名 URL 7 天有效期 | **中** | A02 | 需截获 URL | 文件篡改 |
| F-004 | Token 明文存储 | **中** | A07 | 需本地访问 | 账号接管 |
| F-005 | VNC OTP 无明确过期 | **中** | A07 | 需截获 OTP | 窥视桌面 |
| F-009 | Preload API 过度暴露 | **中** | A01 | 需 XSS | 任意文件读取 |
| F-011 | 文件隔离依赖路径前缀 | **中** | A01 | 需服务端缺陷 | 跨用户数据泄露 |
| F-006 | WS token 在 query 中 | **低** | A07 | 需日志访问 | token 泄露 |
| F-010 | loadURL 无 URL 校验 | **低** | A03 | 需 XSS | 任意页面加载 |
| F-012 | Sidecar 继承完整环境 | **低** | A01 | 需用户操作 | 信息泄露 |
| F-013 | 开发域名泄露 | **低** | A05 | 已公开 | 信息收集 |
| F-014 | 访问控制总结 | **信息** | A01 | - | - |
| F-015 | 密码学总结 | **信息** | A02 | - | - |
| F-016 | 注入风险总结 | **信息** | A03 | - | - |

---

## 八、优先修复建议

### 立即修复（P0）
1. **开启 `webSecurity` 和 `sandbox`**（F-007/F-008）。这是最低成本、最高收益的修复。
2. **淘汰 IAM 静态 Key**（F-001），全面改用 STS 临时凭证。
3. **`readFileAsArrayBuffer` 添加路径白名单**（F-009），仅允许读取用户通过 `dialog.showOpenDialog` 选择的文件。

### 短期修复（P1）
4. Token 改用 `safeStorage` 加密存储（F-004）。
5. 预签名 URL 有效期缩短至 15 分钟（F-003）。
6. VNC OTP 添加 60 秒过期和一次使用限制（F-005）。

### 长期改进（P2）
7. IPC 通道白名单机制（F-009）。
8. WebSocket token 从 query 迁移到 auth 对象（F-006）。
9. Sidecar 添加敏感路径黑名单（F-012）。
10. 生产构建移除开发环境配置（F-013）。

---

> 本报告基于静态代码分析和网络抓包数据，未进行运行时渗透测试。服务端安全性（API 鉴权、输入校验、速率限制）需通过独立审计确认。
