安全模型
Elftia 实现了多层纵深防御的安全架构,覆盖从 Electron 进程隔离到 AI 工具调用审查的完整链路。
安全管线全景
外部消息(Channel 渠道)从输入到执行的完整安全管线:
flowchart TB
Input[Channel 消息输入] --> Sanitize[InputSanitizer<br/>清洗危险字符]
Sanitize --> Rate{RateLimiter<br/>速率检查}
Rate -->|超限| Reject1[拒绝: 429]
Rate -->|通过| Permission{UserPermissionService<br/>用户权限}
Permission -->|blocked| Reject2[拒绝: 403]
Permission -->|guest/moderator/admin| PG{PromptGuardian<br/>提示注入检测}
PG -->|block 模式 + 注入| Reject3[拒绝 + 偏转消息]
PG -->|monitor 模式| Log1[记录日志]
PG -->|通过| Magi[MagiService<br/>Agent 处理]
Magi --> ToolCall[工具调用请求]
ToolCall --> FW{ExecutionFirewall<br/>路径 deny list}
FW -->|拒绝| Block1[拒绝: 路径被阻止]
FW -->|通过| GA{GuardianAgent<br/>AI 安全审查}
GA -->|critical/high| Gate{ChannelPermissionGate<br/>人工确认}
GA -->|low/none| Exec[执行工具]
GA -->|monitor| LogExec[记录 + 执行]
Gate -->|批准| Exec
Gate -->|拒绝| Block2[拒绝: 用户否决]
Exec --> Audit[AuditLogger<br/>审计记录]
LogExec --> Exec
Log1 --> Magi
Electron 安全基础
Context Isolation
Electron 的 Context Isolation 确保渲染进程无法直接访问 Node.js API:
{/* BrowserWindow 创建时的安全配置 */}
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
preload: preloadPath,
},
});
contextBridge
Preload 脚本通过 contextBridge.exposeInMainWorld() 仅暴露白名单 API:
{/* preload/index.ts -- 安全地暴露 IPC 接口 */}
contextBridge.exposeInMainWorld('api', {
completion: {
chatInSession: (params) => ipcRenderer.invoke('completion:chat', params),
},
});
IPC 认证 Token
每次 IPC 调用都携带认证 token,主进程通过 secureHandle 验证:
sequenceDiagram
participant R as Renderer
participant P as Preload
participant M as Main (secureHandle)
Note over R,M: 应用启动
M->>P: ipcRenderer.sendSync('auth:bootstrap') 返回 token
P->>P: 保存 token 到内存
Note over R,M: IPC 调用
R->>P: window.api.someMethod(params)
P->>M: ipcRenderer.invoke(channel, {token, ...params})
M->>M: validateToken(token)
alt Token 无效
M-->>P: throw Error('AUTH_FAILED')
else Token 有效
M->>M: 执行 handler
M-->>P: result
end
- Token 生成: 主进程启动时生成随机 token
- 同步引导: Preload 通过
sendSync在页面加载前获取 token - 每次验证: 所有
secureHandle包装的 IPC 调用都验证 token
ExecutionFirewall
确定性的路径访问控制,阻止访问系统目录和凭据文件。零 LLM 开销。
拒绝规则
{/* 路径规则结构 */}
interface DeniedPathRule {
pattern: RegExp;
reason: string;
denyOps?: ('read' | 'write')[];
}
| 类别 | 示例规则 | 阻止操作 |
|---|---|---|
| 系统目录 | C:\Windows\, /etc/, /usr/, /proc/ | 读 + 写 |
| 凭据文件 | .ssh/, .aws/, .gnupg/, .env | 读 + 写 |
| 浏览器数据 | Chrome\User Data\, .mozilla/ | 读 + 写 |
| 注册表 | System32\config\SAM|SYSTEM|SOFTWARE | 读 + 写 |
| 配置文件 | .gitconfig, .npmrc, .bashrc | 仅写 |
工具路径提取
自动从工具调用参数中提取文件路径进行检查:
{/* 工具到路径参数映射 */}
const FILE_TOOL_PATH_PARAMS: Record<string, string[]> = {
Read: ['path', 'file_path'],
Write: ['path', 'file_path'],
Edit: ['path', 'file_path'],
ListDir: ['path', 'dir_path'],
};
{/* 防火墙检查接口 */}
class ExecutionFirewall {
constructor(workspacePath: string);
checkPath(filePath: string, operation: 'read' | 'write'): FirewallCheckResult;
checkToolCall(toolName: string, toolInput: Record<string, unknown>): FirewallCheckResult;
}
interface FirewallCheckResult {
allowed: boolean;
deniedBy?: string;
reason?: string;
}
GuardianAgent
基于 LLM 的工具调用安全审查器,插在 ExecutionFirewall 之后、人工确认之前。
运行模式
| 模式 | 审查范围 | 阻止策略 | 错误/超时行为 |
|---|---|---|---|
off | 无 | 无 | 不运行 |
monitor | 敏感工具 | 仅记录,不阻止 | fail-open |
guard | 敏感工具 | 阻止 high/critical | fail-open |
strict | 所有工具 | 阻止 medium+ | fail-closed |
风险等级
type RiskLevel = 'none' | 'low' | 'medium' | 'high' | 'critical';
| 等级 | 含义 | 示例 |
|---|---|---|
none | 完全安全 | 读取工作区内文件 |
low | 轻微风险 | 写入项目文件 |
medium | 中等风险 | Shell 命令修改状态、安装包 |
high | 高风险 | 访问敏感路径、工作区外操作 |
critical | 极度危险 | rm -rf、数据外泄、提权 |
关键规则
以下操作始终被评为 high 或 critical:
- 工作区外的文件删除
- 递归删除命令 (
rm -rf,del /s /q,Remove-Item -Recurse) - 访问系统目录 (
/etc,C:\Windows,C:\Users) - 权限提升 (
sudo,runas) - 管道到 Shell (
curl | sh,wget | bash) - 凭据/密钥访问
缓存
使用 SHA-256 哈希缓存已审查的工具调用,避免重复 LLM 调用:
{/* GuardianAgent 缓存键生成 */}
function cacheKey(toolName: string, toolInput: unknown): string {
return createHash('sha256')
.update(JSON.stringify({ tool: toolName, input: toolInput }))
.digest('hex');
}
敏感工具列表
const SENSITIVE_TOOLS = new Set(['Bash', 'Write', 'Edit', 'Agent']);
非敏感工具(如 Read, ListDir)在 monitor/guard 模式下跳过审查,仅在 strict 模式下审查。
PromptGuardian
基于 LLM 的提示注入检测器,用于检测 Channel 消息中的恶意提示注入:
运行模式
| 模式 | 行为 |
|---|---|
off | 禁用 |
monitor | 检测并记录,不阻止 |
block | 检测到注入时阻止并返回偏转消息 |
检测机制
{/* PromptGuardian 核心接口 */}
class PromptGuardian {
async review(content: string, source: MessageSource): Promise<{
allowed: boolean;
isInjection: boolean;
confidence: number;
reason: string;
cached: boolean;
}>;
}
- SHA-256 缓存已检查的内容
- 超时/错误时 fail-open
- 仅对 Channel 来源消息启用
InputSanitizer
正则模式消毒器,移除消息中的危险字符:
{/* 简化的 InputSanitizer */}
class InputSanitizer {
sanitize(input: string): string;
updateConfig(config: SanitizationConfig): void;
}
移除的字符范围:
- 零宽字符 (ZWS, ZWNJ, ZWJ, ZWSP)
- Unicode 方向控制字符
- 不可见格式字符
- 控制字符(保留换行和制表符)
RateLimiter
滑动窗口速率限制器,支持每用户 + 全局限制:
{/* 速率限制器配置 */}
interface RateLimitConfig {
perUser: {
maxRequests: number; // 默认 20
windowMs: number; // 默认 60000 (1 分钟)
};
global: {
maxRequests: number; // 默认 100
windowMs: number; // 默认 60000
};
cleanupIntervalMs: number; // 过期条目清理周期
}
class RateLimiter {
check(userId: string): { allowed: boolean; retryAfter?: number };
updateConfig(config: Partial<RateLimitConfig>): void;
}
UserPermissionService
Channel 用户权限管理,支持自动注册和角色分配:
角色体系
| 角色 | 权限 | 说明 |
|---|---|---|
admin | 全部 | 管理员 |
moderator | 发消息 + 触发 Agent | 协管员 |
guest | 发消息 (受速率限制) | 访客 (默认角色) |
blocked | 无 | 被封禁用户 |
class UserPermissionService {
checkPermission(channelId: string, platformUserId: string): Promise<{
allowed: boolean;
role: UserRole;
user: ChannelUser;
}>;
autoRegister(channelId: string, platformUserId: string, displayName: string): Promise<ChannelUser>;
}
ChannelPermissionGate
Channel 来源的敏感工具调用需要用户确认:
sequenceDiagram
participant Agent as TinyElf Agent
participant Gate as PermissionGate
participant Channel as Channel 平台
participant User as 远端用户
Agent->>Gate: requestPermission(toolCall)
Gate->>Gate: 生成确认指纹 (SHA-256)
Gate->>Channel: 发送确认消息 + 指纹
Channel->>User: Agent 想执行 XX 操作,回复 YES 确认
alt 用户确认
User->>Channel: YES
Channel->>Gate: 匹配指纹
Gate-->>Agent: approved: true
else 超时 (60s)
Gate-->>Agent: approved: false, reason: timeout
else 用户拒绝
User->>Channel: NO
Gate-->>Agent: approved: false, reason: denied
end
AuditLogger
安全审计日志,记录所有安全相关事件到 SQLite audit_log 表:
事件类型
| 事件类型 | 说明 | 严重级别 |
|---|---|---|
tool_executed | 工具调用执行 | info |
tool_blocked | 工具调用被阻止 | warning |
tool_guardian_review | Guardian 审查结果 | info/warning |
permission_requested | 权限确认请求 | info |
permission_granted | 权限被批准 | info |
permission_denied | 权限被拒绝 | warning |
rate_limited | 速率限制触发 | warning |
injection_detected | 提示注入检测 | critical |
user_blocked | 用户被封禁 | warning |
{/* 审计日志条目 */}
interface AuditLogEntry {
id: string;
timestamp: number;
eventType: AuditEventType;
severity: 'info' | 'warning' | 'critical';
channelId?: string;
userId?: string;
toolName?: string;
details: Record<string, unknown>;
}
SecurityService / CryptoService
应用级加密服务,保护存储在本地的敏感数据:
{/* 加密方案 */}
class SecurityService {
encrypt(plaintext: string): string; // AES-256-GCM, IV + authTag + ciphertext
decrypt(ciphertext: string): string;
}
class CryptoService {
deriveKey(password: string, salt: Buffer): Buffer;
// PBKDF2, 100K iterations, SHA-512, 32 bytes (256 bits)
}
所有 API 密钥在存储到 SQLite 前都经过 SecurityService.encrypt() 加密。
相关文件
| 文件 | 说明 |
|---|---|
packages/desktop/app/main/services/platform/security/ExecutionFirewall.ts | 路径防火墙 |
packages/desktop/app/main/services/platform/security/GuardianAgent.ts | AI 工具审查 |
packages/desktop/app/main/services/platform/security/PromptGuardian.ts | 提示注入检测 |
packages/desktop/app/main/services/platform/security/RateLimiter.ts | 速率限制 |
packages/desktop/app/main/services/platform/security/InputSanitizer.ts | 输入消毒 |
packages/desktop/app/main/services/platform/security/UserPermissionService.ts | 用户权限 |
packages/desktop/app/main/services/platform/security/ChannelPermissionGate.ts | 权限确认门 |
packages/desktop/app/main/services/platform/security/AuditLogger.ts | 审计日志 |
packages/desktop/app/main/services/platform/security/SecurityService.ts | AES-256-GCM 加密 |
packages/desktop/app/main/services/infra/crypto/CryptoService.ts | PBKDF2 密钥派生 |
packages/desktop/app/main/ipc/safe-handle.ts | IPC 安全句柄 |
packages/desktop/app/preload/index.ts | contextBridge 接口暴露 |
packages/desktop/app/shared/contracts/security-types.ts | 安全相关共享类型 |
packages/desktop/app/main/workers/db/auditLog.ts | 审计日志 DB 操作 |
packages/desktop/app/main/workers/db/channelUsers.ts | Channel 用户 DB 操作 |