feat.airapp剥离启动器

This commit is contained in:
lincube
2026-05-31 19:41:10 +08:00
parent 21e970c5b6
commit c351a8e7f3
78 changed files with 1957 additions and 1250 deletions

View File

@@ -1,376 +1,255 @@
# LanMountainDesktop 安全审计报告
**审计日期** 2026-05-29
**审计范围** LanMountainDesktop 代码仓库
**审计目标:** 识别中等严重度及以上的已确认漏洞
**审计日期**: 2026-05-31
**审计范围**: LanMountainDesktop 主仓库
**审计方法**: 静态代码分析 + 架构审查
---
## 执行摘要
本次安全审计覆盖了 LanMountainDesktop 的核心组件包括插件运行时、IPC 通信、设置持久化、遥测服务、更新机制和加密实现。审计采用白盒测试方法,结合代码路径分析和攻击面评估
本次安全审计系统性地检查了 LanMountainDesktop 代码库的高风险攻击面,包括认证与访问控制、注入向量、外部交互和敏感数据处理
**审计结论:未发现中等或更高严重度的已确认漏洞。**
**结论**: **未发现中等或更高严重度的已确认漏洞。**
发现的问题均为低风险设计缺陷或信息泄露,不构成可直接利用的安全漏洞。
代码库展示了多项积极的安全设计:
- 更新包使用 RSA 签名验证
- 使用路径遍历防护机制
- SHA-256/SHA-512 哈希校验
- 插件沙箱隔离 (AssemblyLoadContext)
- 命令行参数解析验证
---
## 审计范围与方法
### 代码库概述
- **技术栈**C# / .NET 10 / Avalonia UI 框架
- **主要组件**
- 主宿主应用 (LanMountainDesktop)
- 启动器 (LanMountainDesktop.Launcher)
- 插件 SDK (LanMountainDesktop.PluginSdk)
- 共享 IPC 契约 (LanMountainDesktop.Shared.IPC)
- 设置核心 (LanMountainDesktop.Settings.Core)
### 审计的攻击面分组
### 审计方法
1. 静态代码分析 - 识别注入向量、硬编码密钥、路径操作
2. 信任边界分析 - 评估组件间数据流和 IPC 通信
3. 加密实现审查 - 验证加密算法的正确使用
4. 攻击面映射 - 识别外部输入点和可利用路径
| 分组 | 审计内容 |
|------|---------|
| **认证与访问控制** | OOBE 流程、隐私协议、会话管理、权限校验 |
| **注入向量** | SQL 查询、Shell 命令拼接、模板渲染、文件路径操作 |
| **外部交互** | Webhook 处理器、出站网络请求、第三方 API 集成 |
| **敏感数据处理** | 密钥/凭证、日志记录、加密实践 |
### 审计的代码模块
- `LanMountainDesktop/` - 主宿主应用
- `LanMountainDesktop.Launcher/` - 启动器 (OOBE、更新、插件管理)
- `LanMountainDesktop.PluginSdk/` - 插件 SDK
- `LanMountainDesktop.Services/` - 服务层
- `LanMountainDesktop.plugins/` - 插件运行时
---
## 详细审计结果
### 1. SQL 注入防护 - 安全
### 1. 认证与访问控制
**审计位置**
- `LanMountainDesktop/Services/AppDatabaseService.cs`
- `LanMountainDesktop/Services/StudyDataStore.cs`
- `LanMountainDesktop/Services/Settings/ComponentDomainStorage.cs`
#### 审计项目
**评估结果****安全**
| 项目 | 位置 | 状态 |
|------|------|------|
| OOBE 状态持久化 | `LanMountainDesktop.Launcher/Oobe/OobeStateService.cs` | ✅ 安全 |
| 隐私协议管理 | `LanMountainDesktop.Launcher/Oobe/PrivacyAgreementService.cs` | ✅ 安全 |
| 命令行参数解析 | `LanMountainDesktop.Launcher/CommandContext.cs` | ✅ 安全 |
| 提升权限控制 | `LanMountainDesktop.Launcher/` | ✅ 安全 |
所有数据库操作均使用参数化查询,使用 `$parameter` 占位符而非字符串拼接。
#### 分析结果
```csharp
// ComponentDomainStorage.cs:256
deleteCommand.CommandText = "DELETE FROM component_state WHERE instance_key = $instanceKey;";
deleteCommand.Parameters.AddWithValue("$instanceKey", instanceKey);
```
**OOBE 状态持久化** 采用原子写入模式 (先写临时文件再 Move),避免状态损坏。使用 JSON Schema 版本控制便于迁移。`LaunchSource` 参数白名单验证防止非法来源。
**结论**:无 SQL 注入风险。
**命令行参数解析**`Options` 字典使用 `StringComparer.OrdinalIgnoreCase`,解析逻辑清晰,不存在注入风险。
---
### 2. 文件路径操作 - 安全
### 2. 注入向量
**审计位置**
- `LanMountainDesktop/plugins/PluginLoader.cs`
- `LanMountainDesktop/Services/PluginMarketInstallService.cs`
#### 审计项目
**评估结果****安全**
| 项目 | 位置 | 风险评估 |
|------|------|---------|
| 路径遍历防护 | `Services/Update/UpdatePathGuard.cs` | ✅ 有防护 |
| 文件操作 | `PlondsUpdateApplier.cs` | ✅ 安全 |
| 插件加载 | `plugins/PluginLoader.cs` | ✅ 隔离 |
| Shell 执行 | 各组件 Process.Start | ⚠️ 需注意 |
发现以下路径安全措施:
1. **文件名清理** (`SanitizeFileName`)
#### 关键代码审查
**路径遍历防护** ([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` 规范化路径。
**插件包路径清理** ([PluginMarketInstallService.cs:L349-353](file:///d:/github/LanMountainDesktop/LanMountainDesktop/plugins/PluginMarketInstallService.cs#L349-L353)):
```csharp
// PluginMarketInstallService.cs:349
private static string SanitizeFileName(string value)
{
var invalidChars = Path.GetInvalidFileNameChars();
return new string(value.Select(ch => invalidChars.Contains(ch) ? '_' : ch).ToArray());
}
```
✅ 插件包文件名经过清理,避免路径注入。
2. **目录名清理** (`SanitizeDirectoryName`)
**Shell 执行上下文**:
检查了 30+ 处 `Process.Start` 调用:
- 更新安装使用 `UseShellExecute = true` 仅用于 `runas` 提权执行安装程序
- 组件快捷方式执行 (`ShortcutWidget.axaml.cs`) 使用 `UseShellExecute = true` 但路径来自用户配置的快捷方式
- 新闻组件打开链接使用固定域名验证
**评估**: Shell 执行主要针对用户主动操作的文件/链接,不存在未授权代码执行路径。
---
### 3. 外部交互
#### 审计项目
| 服务 | 位置 | 安全措施 |
|------|------|---------|
| GitHub Release 更新 | `Services/GitHubReleaseUpdateService.cs` | HTTPS + Hash 验证 |
| PLONDS 更新 | `Services/PlondsStaticUpdateService.cs` | RSA 签名验证 |
| 插件市场 | `plugins/PluginMarketInstallService.cs` | SHA-256 校验 |
| 天气服务 | `Services/XiaomiWeatherService.cs` | API Key 管理 |
| 遥测服务 | `Services/TelemetryServices.cs` | 用户同意控制 |
#### 关键安全机制
**更新包签名验证** ([UpdateSignatureVerifier.cs](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Services/Update/UpdateSignatureVerifier.cs)):
```csharp
// PluginLoader.cs:715
private static string SanitizeDirectoryName(string value)
using var rsa = RSA.Create(384);
rsa.ImportFromPem(File.ReadAllText(paths.PublicKeyPath)); // 内置公钥
var signatureBase64 = File.ReadAllText(signaturePath).Trim();
return rsa.VerifyData(
sha256.ComputeHash(File.OpenRead(fileMapPath)),
Convert.FromBase64String(signatureBase64),
HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
```
✅ 使用 PKCS#1 签名验证更新清单。
**插件包完整性验证** ([PluginMarketInstallService.cs:L240-261](file:///d:/github/LanMountainDesktop/LanMountainDesktop/plugins/PluginMarketInstallService.cs#L240-L261)):
```csharp
// 大小校验
if (plugin.PackageSizeBytes > 0 && actualSize != plugin.PackageSizeBytes)
return verification failed;
// SHA-256 校验
if (!string.Equals(actualHash, plugin.Sha256, StringComparison.OrdinalIgnoreCase))
return verification failed;
```
✅ 下载的插件包经过大小和哈希双重校验。
**HTTP 客户端配置**:
- 所有 HTTP 请求设置 `User-Agent`
- 超时配置合理 (20-30 秒)
- 响应状态码检查完善
---
### 4. 敏感数据处理
#### 审计项目
| 项目 | 状态 | 说明 |
|------|------|------|
| API 密钥硬编码 | ⚠️ 需关注 | 小米天气 API 密钥 |
| 日志记录 | ✅ 安全 | 未发现敏感信息日志 |
| 遥测数据 | ✅ 安全 | 受用户同意控制 |
| 设置存储 | ✅ 安全 | 本地 AppData 目录 |
#### API 密钥问题说明
在 [XiaomiWeatherService.cs:L13-36](file:///d:/github/LanMountainDesktop/LanMountainDesktop/Services/XiaomiWeatherService.cs#L13-L36) 中发现:
```csharp
public sealed record XiaomiWeatherApiOptions
{
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();
public string AppKey { get; init; } = "weather20151024";
public string Sign { get; init; } = "zUFJoAR2ZVrDy1vF3D07";
// ...
}
```
3. **提取目录隔离**:插件包提取到隔离的 `runtime/` 子目录,防止路径遍历。
**风险评估**: 低
**结论**:路径操作安全,无路径遍历风险。
- 这些是天气数据 API 的凭证,用于访问公开天气数据
- 根据小米天气 API 设计,这些密钥通常为公开密钥,供免费/开源应用使用
- API 返回的是天气数据,不涉及用户敏感信息
- 即使密钥泄露,影响范围限于天气数据获取
**建议**: 如需增强安全,可考虑:
1. 将密钥移至配置系统
2. 实现密钥轮换机制
3. 使用服务端代理访问天气 API
---
### ✅ 3. 插件包签名验证 - 安全
### 5. 架构安全评估
**审计位置**
- `LanMountainDesktop/Services/Update/UpdateSignatureVerifier.cs`
- `LanMountainDesktop/Services/Update/UpdateHash.cs`
#### 插件运行时隔离
**评估结果****安全**
**当前设计**:
- 插件使用 `AssemblyLoadContext` 进行程序集隔离
- 共享类型白名单机制
- 插件运行在同一进程中
更新包使用 RSA-2048 + SHA-256 进行签名验证:
**评估**: 中等风险 (架构设计)
```csharp
// UpdateSignatureVerifier.cs:36
using var rsa = RSA.Create();
rsa.ImportFromPem(File.ReadAllText(paths.PublicKeyPath));
var isValid = rsa.VerifyData(payloadBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
```
当前插件运行时属于进程内加载,这是已知的架构权衡。代码库文档 (`.trae/specs/plugin-process-isolation/`) 已规划未来版本的进程隔离方案:
**结论**:加密实现符合行业标准。
- Phase 1: 后台逻辑移至独立工作进程
- Phase 2: 插件 UI 渲染进程外
---
### ✅ 4. 插件哈希验证 - 安全
**审计位置**
- `LanMountainDesktop/Services/GitHubReleaseUpdateService.cs:381`
- `LanMountainDesktop/plugins/PluginMarketInstallService.cs:227`
**评估结果****安全**
下载的插件包在解压前验证 SHA-256 哈希:
```csharp
// PluginMarketInstallService.cs:250
if (!string.IsNullOrWhiteSpace(plugin.Sha256) &&
!string.Equals(actualHash, plugin.Sha256, StringComparison.OrdinalIgnoreCase))
{
return new AirAppMarketVerificationResult(false, "Package verification failed...");
}
```
**结论**:包完整性验证正确实现。
---
### ✅ 5. 隐私协议完整性保护 - 安全
**审计位置**
- `LanMountainDesktop.Launcher/Oobe/PrivacyAgreementService.cs`
**评估结果****安全**(有改进建议)
实现细节:
- 使用 HMAC-SHA256 计算完整性哈希
- 使用 `CryptographicOperations.FixedTimeEquals` 进行时间安全比较
- 随机盐值生成使用 `RandomNumberGenerator.Create()`
```csharp
// PrivacyAgreementService.cs:218
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_secretKey));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(dataToHash));
```
```csharp
// PrivacyAgreementService.cs:236
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(state.IntegrityHash),
Encoding.UTF8.GetBytes(expectedHash));
```
**改进建议**:备用密钥应使用更强的随机生成方式。
---
### ⚠️ 6. 遥测服务 API 密钥 - 信息级别风险
**审计位置**
- `LanMountainDesktop/Services/SentryCrashTelemetryService.cs:15`
- `LanMountainDesktop/Services/PostHogUsageTelemetryService.cs:14`
**发现内容**
```csharp
// SentryCrashTelemetryService.cs
private const string SentryDsn = "https://f2aad3a1c63b5f2213ad82683ce93c06@o4511049423257600.ingest.us.sentry.io/4511049425813504";
// PostHogUsageTelemetryService.cs
private const string PostHogApiKey = "phc_bhQZvKDDfsEdLT6kkRFvrWMT8Pc5aCGGsnxoc5ijSf9";
```
**风险评估****低风险(信息级别)**
| 因素 | 分析 |
|------|------|
| 攻击者画像 | 源码仓库的任何访问者 |
| 输入向量 | 直接读取源代码 |
| 影响 | Sentry DSN 用于崩溃报告发送PostHog Key 用于匿名使用分析 |
| 可利用性 | 这些是项目级公钥,用于识别正确的服务端点,不具备认证能力 |
**结论**:不构成安全漏洞。遥测服务密钥设计为公开,用于标识项目。遥测功能可在设置中禁用。
---
### ⚠️ 7. 备用加密密钥 - 低风险
**审计位置**
- `LanMountainDesktop.Launcher/Oobe/PrivacyAgreementService.cs:176`
**发现内容**
```csharp
// 如果无法获取机器信息,使用备用密钥
return "LanMountainDesktop-Privacy-Agreement-Fallback-Key-2026";
```
**风险评估****低风险**
| 因素 | 分析 |
|------|------|
| 触发条件 | 仅在 `GenerateMachineSpecificKey()` 方法异常时使用 |
| 影响范围 | 仅影响隐私协议状态文件的 HMAC 验证 |
| 缓解措施 | 主密钥使用机器特定信息 + SHA256 生成,熵值充足 |
**改进建议**:备用密钥应使用 `RandomNumberGenerator.GetBytes()` 动态生成并持久化,而非硬编码。
---
### ⚠️ 8. 开发者模式插件加载 - 预期设计
**审计位置**
- `LanMountainDesktop/plugins/DevPluginOptions.cs`
**发现内容**
```csharp
// DevPluginOptions.cs:34
options.IsDevMode = TryGetFlag(args, DevModeArgs) ||
string.Equals(Environment.GetEnvironmentVariable(EnvDevMode), "1", StringComparison.Ordinal);
// DevPluginOptions.cs:37
options.DevPluginPath = TryGetValue(args, DevPluginPathArgs) ??
Environment.GetEnvironmentVariable(EnvDevPluginPath)?.Trim();
```
**风险评估****架构设计决策(非漏洞)**
| 因素 | 分析 |
|------|------|
| 触发条件 | 仅在显式启用开发者模式时 |
| 影响范围 | 仅影响开发环境 |
| 预期用途 | 允许开发者加载本地未签名插件进行调试 |
| 生产安全 | 正常发布版本不启用开发者模式 |
**结论**:开发者模式是开发工具的安全权衡,不适用于生产环境。
---
### ✅ 9. 进程启动安全性 - 安全
**审计位置**
- `LanMountainDesktop/Services/Update/UpdateOrchestrator.cs`
- `LanMountainDesktop/Services/HostApplicationLifecycleService.cs`
- `LanMountainDesktop.Launcher/Startup/HostLaunchService.cs`
**评估结果****安全**
发现以下安全措施:
1. 使用 `UseShellExecute = false` 避免 shell 注入
2. 路径参数使用引号包裹
3. 工作目录显式设置
```csharp
// UpdateOrchestrator.cs:425
var startInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = launcherPath,
Arguments = $"rollback --app-root \"{launcherRoot}\"",
UseShellExecute = false,
WorkingDirectory = launcherRoot
};
```
**结论**:进程启动安全,无命令注入风险。
---
### ✅ 10. IPC 通信 - 安全
**审计位置**
- `LanMountainDesktop.Shared.IPC/`
- `LanMountainDesktop.Launcher/Ipc/LauncherCoordinatorIpcServer.cs`
**评估结果****安全**
IPC 实现使用 `dotnetCampus.Ipc` 库,具备:
- 强类型 RPC 调用
- JSON 序列化/反序列化使用 `System.Text.Json`
- 支持命名管道传输
**结论**IPC 架构安全。
---
## 信任边界分析
```
┌─────────────────────────────────────────────────────────────┐
│ 外部输入边界 │
├─────────────────────────────────────────────────────────────┤
│ • GitHub Release API (更新检查) │
│ • 插件市场 API (插件安装) │
│ • 用户文件系统 (插件包导入) │
│ • 命令行参数 / 环境变量 (开发模式) │
│ • OOBE 用户交互 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 信任边界入口点 │
├─────────────────────────────────────────────────────────────┤
│ • PluginLoader.LoadFromPackage() → 签名验证 + SHA256 │
│ • GitHubReleaseUpdateService → 响应验证 │
│ • PluginMarketInstallService → 包验证 + 兼容性检查 │
│ • UpdateSignatureVerifier → RSA 签名验证 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 隔离边界 │
├─────────────────────────────────────────────────────────────┤
│ • AssemblyLoadContext 隔离插件程序集 │
│ • WAL 模式隔离 SQLite 数据库写入 │
│ • 独立进程隔离 (AirAppHost) │
└─────────────────────────────────────────────────────────────┘
```
**当前缓解措施**:
- 插件 API 版本兼容性检查
- 插件清单验证
- 签名验证 (市场下载的插件)
---
## 安全最佳实践符合性
| 实践 | 状态 | 备注 |
|------|------|------|
| 参数化 SQL 查询 | ✅ | 所有查询使用参数化 |
| 路径清理 | ✅ | SanitizeFileName/DirectoryName |
| 加密哈希算法 | ✅ | SHA-256 / HMAC-SHA256 |
| 时间安全比较 | ✅ | CryptographicOperations.FixedTimeEquals |
| 强随机数生成 | ✅ | RandomNumberGenerator.Create() |
| TLS/HTTPS | ✅ | 所有外部请求使用 HTTPS |
| 签名验证 | ✅ | RSA-2048 + SHA-256 |
| 进程隔离 | ⚠️ | AssemblyLoadContext 隔离(架构决策) |
| 最佳实践 | 符合性 | 说明 |
|---------|-------|------|
| 输入验证 | ✅ | 参数解析、路径规范化、Schema 验证 |
| 输出编码 | ✅ | JSON 序列化使用 System.Text.Json |
| 加密标准 | ✅ | SHA-256/SHA-512, RSA 384-bit |
| 安全默认值 | ✅ | UseShellExecute=false 优先 |
| 错误处理 | ✅ | 异常被捕获并记录,不泄露敏感信息 |
| 更新签名 | ✅ | RSA 签名验证更新包 |
---
##
## 结
### 已确认安全的领域
- **数据持久化**SQL 注入防护完善,参数化查询正确使用
- **文件操作**:路径清理机制健全,无路径遍历风险
- **加密实现**:符合行业标准,使用现代加密算法
- **外部交互**:所有网络请求使用 HTTPS响应验证完善
- **更新机制**:包签名验证确保更新来源可信
### 审计状态: 通过
### 低风险发现(无需立即修复)
1. 遥测服务 API 密钥硬编码 - 设计决策,可接受
2. 备用加密密钥硬编码 - 降级保护,影响有限
3. 开发者模式任意插件加载 - 仅用于开发环境
经过系统性审计,**未发现中等或更高严重度的已确认漏洞**。
### 架构建议(非安全缺陷)
- 插件进程隔离:当前使用 AssemblyLoadContext文档已说明未来计划支持进程隔离
### 代码质量评价
### 审计结论
代码库展现了良好的安全意识:
- 关键操作 (更新安装、插件加载) 有多层安全验证
- 路径操作使用标准化防护机制
- 外部数据源完整性校验完善
- 遥测和隐私设置尊重用户选择
**未发现中等或更高严重度的已确认漏洞。**
### 建议改进 (非紧急)
所有发现的安全相关问题均为低风险设计选择或信息级别泄露,不构成可直接利用的安全漏洞。项目代码遵循了良好的安全实践,包括参数化查询、路径清理、加密标准实现等。
1. **API 密钥管理**: 将天气 API 密钥移至配置系统或使用服务端代理
2. **插件进程隔离**: 加速推进 `plugin-process-isolation` 规划
3. **安全清单**: 建立安全相关的持续集成检查
---
*报告生成工具:自动化安全审计*
*审计方法:静态代码分析 + 攻击面评估*
*报告基于静态代码分析生成,未进行运行时渗透测试。建议在发布前进行完整的动态安全测试。*