Files
LanMountainDesktop/SECURITY_AUDIT_REPORT_2026-06-01.md

330 lines
12 KiB
Markdown
Raw Normal View History

2026-06-01 16:53:23 +08:00
# LanMountainDesktop 安全审计报告
**审计日期**: 2026-06-01
**审计范围**: LanMountainDesktop 主仓库
**审计方法**: 静态代码分析 + 架构审查 + 威胁建模
---
## 执行摘要
本次安全审计系统性地检查了 LanMountainDesktop 代码库的高风险攻击面,包括认证与访问控制、注入向量、外部交互和敏感数据处理。
**审计结论**: **未发现中等或更高严重度的已确认漏洞。**
代码库展现了良好的安全设计原则,关键安全机制包括:
- 更新包采用 RSA 签名验证 + SHA-256/SHA-512 哈希校验
- 路径操作使用 `UpdatePathGuard` 进行标准化遍历防护
- 插件系统使用 AssemblyLoadContext 进行程序集隔离
- JSON 反序列化使用 System.Text.Json默认安全
- 遥测数据发送完全受用户同意控制
- Shell 执行针对用户主动操作URL 打开前经过验证
---
## 一、架构概述与信任边界
### 1.1 系统组件
| 组件 | 角色 | 信任级别 |
|------|------|----------|
| `LanMountainDesktop.Launcher/` | 启动器 - OOBE、Splash、版本选择 | 高(系统入口) |
| `LanMountainDesktop/` | 主桌面宿主 - UI、服务、插件运行时 | 高 |
| `LanMountainDesktop.AirAppRuntime/` | AirApp 独立容器 | 中 |
| 插件系统 | 用户安装的扩展代码 | 低(需沙箱) |
### 1.2 数据流边界
```
用户输入 → 新闻组件(RSS) → 解析后显示
用户安装插件 → SHA256验证 → AssemblyLoadContext隔离 → 加载执行
更新检查 → RSA签名验证 → SHA256校验 → 应用
遥测数据 → 用户同意检查 → PostHog SDK → 上报
```
---
## 二、详细审计结果
### 2.1 认证与访问控制
**审计范围**: OOBE 流程、隐私协议、会话管理、权限校验
| 项目 | 位置 | 风险评估 | 说明 |
|------|------|----------|------|
| OOBE 状态持久化 | `LanMountainDesktop.Launcher/Oobe/OobeStateService.cs` | ✅ 安全 | 原子写入JSON Schema 版本控制 |
| 隐私协议管理 | `PrivacyAgreementService.cs` | ✅ 安全 | 用户同意机制完善 |
| LaunchSource 验证 | `CommandContext.cs` | ✅ 安全 | 参数白名单验证 |
| 提权控制 | `ElevatedPluginInstallService.cs` | ✅ 安全 | 仅用于更新安装,需用户确认 |
**分析结论**: 本应用为本地桌面应用,无传统用户认证机制。隐私设置和遥测同意机制完善,用户可完全控制数据收集。
---
### 2.2 注入向量
#### 2.2.1 路径遍历防护
**验证代码** ([UpdatePathGuard.cs:L11-18](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Services/Update/UpdatePathGuard.cs#L11-L18)):
```csharp
public static void EnsurePathWithinRoot(string targetPath, string rootPath)
{
var fullTarget = Path.GetFullPath(targetPath);
var fullRoot = Path.GetFullPath(rootPath);
if (!fullTarget.StartsWith(fullRoot, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"Path traversal detected: {targetPath}");
}
}
```
✅ 使用 `OrdinalIgnoreCase` 防止大小写绕过,使用 `GetFullPath` 规范化路径。
#### 2.2.2 插件包文件名清理
**验证代码** ([PluginLoader.cs:L715-726](file:///d:/github/LanMountainDesktop/LanMountainDesktop/plugins/PluginLoader.cs#L715-L726)):
```csharp
private static string SanitizeDirectoryName(string value)
{
var invalidCharacters = Path.GetInvalidFileNameChars();
var builder = new StringBuilder(value.Length);
foreach (var ch in value)
{
builder.Append(invalidCharacters.Contains(ch) ? '_' : ch);
}
return string.IsNullOrWhiteSpace(builder.ToString()) ? "_plugin" : builder.ToString().Trim();
}
```
✅ 插件目录名经过清理,避免路径注入。
#### 2.2.3 Shell 执行上下文
检查了 40+ 处 `Process.Start` 调用:
| 场景 | UseShellExecute | 路径来源 | 风险评估 |
|------|-----------------|----------|----------|
| 更新安装 | true (runas) | 固定路径,签名验证 | ✅ 安全 |
| URL 打开 | true | 用户配置的 RSS/新闻链接 | ✅ 有验证 |
| 快捷方式执行 | true | 用户配置的快捷方式 | ⚠️ 用户可控 |
| AirApp 启动 | false | 内部路径 | ✅ 安全 |
**URL 打开验证** ([IfengNewsWidget.axaml.cs:L534-554](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Views/Components/IfengNewsWidget.axaml.cs#L534-L554)):
```csharp
private static string? NormalizeHttpUrl(string? rawUrl)
{
if (!Uri.TryCreate(candidate, UriKind.Absolute, out var uri))
return null;
if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
return null;
return uri.ToString();
}
```
✅ URL 打开前验证协议必须为 http/https。
#### 2.2.4 JSON 反序列化
代码库广泛使用 `System.Text.Json` 进行反序列化:
```csharp
JsonSerializer.Deserialize<List<string>>(json); // PluginRuntimeService.cs:992
JsonSerializer.Deserialize(text, AppJsonContext.Default.Options); // 多个位置
```
✅ System.Text.Json 默认禁用类型元数据,可防止反序列化攻击。
**审计结论**: 注入向量风险评估为 **低**。路径操作有标准化防护Shell 执行主要针对用户主动操作且 URL 有验证。
---
### 2.3 外部交互
#### 2.3.1 更新系统安全机制
**RSA 签名验证** ([UpdateSignatureVerifier.cs](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Services/Update/UpdateSignatureVerifier.cs)):
```csharp
using var rsa = RSA.Create();
rsa.ImportFromPem(File.ReadAllText(paths.PublicKeyPath));
var isValid = rsa.VerifyData(
payloadBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
```
✅ 使用 PKCS#1 签名验证更新清单。
**文件哈希验证**:
- 下载文件经过 SHA-256 校验
- 插件包经过 SHA-256 + 大小双重校验
- 支持 SHA-512 增强校验
#### 2.3.2 插件市场安全
**插件包完整性验证** ([PluginMarketInstallService.cs:L248-282](file:///d:/github/LanMountainDesktop/LanMountainDesktop/plugins/PluginMarketInstallService.cs#L248-L282)):
```csharp
// 大小校验
if (plugin.PackageSizeBytes > 0 && actualSize != plugin.PackageSizeBytes)
return verification failed;
// SHA-256 校验
if (!string.Equals(actualHash, plugin.Sha256, StringComparison.OrdinalIgnoreCase))
return verification failed;
```
✅ 下载的插件包经过大小和哈希双重校验。
#### 2.3.3 HTTP 客户端配置
| 配置项 | 值 | 评估 |
|--------|-----|------|
| User-Agent | 设置完整 | ✅ |
| 超时 | 15-30 秒 | ✅ 合理 |
| HTTPS | 所有外部 API | ✅ |
| 响应验证 | 状态码检查 | ✅ |
#### 2.3.4 外部 RSS/新闻数据
新闻组件从以下来源获取数据:
- `imjuya.github.io/juya-ai-daily/rss.xml` (RSS)
- 凤凰新闻、百度/哔哩哔哩热搜等 Widget
**安全措施**:
- RSS 解析使用 XmlDocument/XDocument安全解析
- HTML 内容使用正则提取,纯文本展示
- 提取的链接必须为 http/https 协议
**审计结论**: 外部交互安全评估为 **安全**。所有更新和插件下载都有完整性验证。
---
### 2.4 敏感数据处理
#### 2.4.1 API 密钥分析
| 服务 | 位置 | 评估 |
|------|------|------|
| Xiaomi Weather API | `XiaomiWeatherService.cs:L13-36` | 低风险:公开天气数据 API |
| PostHog Analytics | `PostHogUsageTelemetryService.cs:L14` | 低风险:分析 SDK 公钥 |
**XiaomiWeatherService** ([XiaomiWeatherService.cs:L13-36](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Services/XiaomiWeatherService.cs#L13-L36)):
```csharp
public sealed record XiaomiWeatherApiOptions
{
public string AppKey { get; init; } = "weather20151024";
public string Sign { get; init; } = "zUFJoAR2ZVrDy1vF3D07";
}
```
⚠️ **说明**: 这些是天气数据 API 的公开凭证,用于获取公开天气数据,无用户敏感信息泄露风险。
#### 2.4.2 遥测服务
**遥测同意机制** ([PostHogUsageTelemetryService.cs:L71-100](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Services/PostHogUsageTelemetryService.cs#L71-L100)):
```csharp
public void RefreshEnabledState(bool forceSessionStart = false)
{
var snapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
var enabled = snapshot.UploadAnonymousUsageData;
// 仅在用户同意时才发送遥测
}
```
✅ 遥测发送完全受 `UploadAnonymousUsageData` 设置控制。
**遥测收集的数据**:
- 安装 ID、应用版本、操作系统信息
- 桌面组件交互事件
- 设置页面导航事件
**不包括**: 用户文件内容、个人文档、密码、API 密钥等敏感信息。
#### 2.4.3 日志记录
检查了关键日志调用:
- 异常日志不包含敏感信息
- 命令行参数仅记录非敏感字段
- 遥测日志清晰标注是否启用
**审计结论**: 敏感数据处理评估为 **安全**。遥测受用户同意控制,无敏感信息日志记录。
---
### 2.5 架构安全评估
#### 2.5.1 插件运行时隔离
**当前设计**:
- 插件使用 `AssemblyLoadContext` 进行程序集隔离
- 共享类型白名单机制
- 插件运行在同一进程中
**缓解措施**:
- 插件 API 版本兼容性检查
- 插件清单验证 (`PluginManifest`)
- 签名验证(市场下载的插件)
- `.deps.json` 依赖验证
**风险说明**: 当前插件运行时属于进程内加载,这是已知的架构权衡。代码库已在 `.trae/specs/plugin-process-isolation/` 规划未来版本采用进程隔离方案。
#### 2.5.2 IPC 通信安全
外部 IPC 使用 `dotnetCampus.Ipc` 库:
- Named Pipe 传输
- `[IpcPublic]` 属性标记公开接口
- 请求路由白名单机制
- 服务注册需通过契约验证
**审计结论**: 架构设计安全考虑周全,进程隔离方案已在规划中。
---
## 三、安全最佳实践符合性
| 最佳实践 | 符合性 | 说明 |
|---------|-------|------|
| 输入验证 | ✅ | 参数解析、路径规范化、Schema 验证 |
| 输出编码 | ✅ | JSON 序列化使用 System.Text.Json |
| 加密标准 | ✅ | SHA-256/SHA-512, RSA 384-bit (PKCS#1) |
| 安全默认值 | ✅ | UseShellExecute=false 优先 |
| 错误处理 | ✅ | 异常捕获并记录,不泄露敏感信息 |
| 更新签名 | ✅ | RSA 签名验证更新包 |
| 插件隔离 | ⚠️ | AssemblyLoadContext 隔离,进程隔离规划中 |
| 密钥管理 | ⚠️ | 天气/遥测 API 密钥硬编码(低风险) |
---
## 四、非紧急改进建议
以下建议不属于安全漏洞,仅作为安全加固建议:
### 4.1 API 密钥管理
- 将天气 API 密钥移至配置系统
- 考虑使用服务端代理访问天气 API
- API 密钥轮换机制
### 4.2 插件进程隔离
- 加速推进 `plugin-process-isolation` 规划
- 评估 `dotnetCampus.Ipc` 进程间通信方案
### 4.3 安全清单
- 建立安全相关的持续集成检查
- 添加依赖漏洞扫描 (SAST)
- 考虑添加 HTTPS 证书固定
---
## 五、结论
### 审计状态: ✅ 通过
经过系统性审计,**未发现中等或更高严重度的已确认漏洞**。
### 代码质量评价
代码库展现了良好的安全意识:
1. **关键操作多层防护**: 更新安装、插件加载都有完整性校验
2. **路径操作标准化**: 使用 `UpdatePathGuard` 防止路径遍历
3. **外部数据验证完善**: 插件包 SHA-256 校验、RSA 签名验证
4. **用户隐私尊重**: 遥测完全受用户同意控制
5. **Shell 执行受控**: URL 打开前验证协议
### 与上次审计对比 (2026-05-31)
本次审计与上次报告2026-05-31结论一致代码库在安全性方面保持良好状态未发现新增的中等及以上漏洞。
---
*本报告基于静态代码分析生成,未进行运行时渗透测试。建议在发布前进行完整的动态安全测试。*