# 漏洞总结 ## 漏洞概述 - **漏洞编号**: #860 - **漏洞类型**: 身份验证绕过 + 默认允许所有权限的RBAC(基于角色的访问控制) - **严重程度**: 高危 - **描述**: 修复了一个3步漏洞链,该漏洞链允许未授权/提权访问管理接口 ## 影响范围 - 影响所有未正确分类的API方法 - 攻击者可以通过无效令牌或过期令牌绕过身份验证 - 未分类的方法默认返回可写角色(admin/write/read) - 多个敏感方法未包含在允许列表中,通过默认允许策略被跳过 ## 修复方案 1. **身份验证修复**: 拒绝无效令牌,即使令牌存在但已过期 2. **默认拒绝策略**: 未分类的方法现在默认为"拒绝"而非"允许" 3. **方法分类**: 将敏感方法明确分类为admin/write/read权限 4. **额外加固**: - 移除isWriteMethod前缀回退 - 使用精确的allowlist防止静默范围蔓延 - 添加Phase-3方法(TTS、浏览器、Zalo、WhatsApp)到admin/write/read权限分类 - 在路由器调度器中删除空MethodRole ## 关键代码变更 ### 身份验证修复 (router.go) ```go // 修复前:无效令牌静默通过 // 修复后:拒绝无效令牌 if pe := r.server.policyEngine; pe != nil { if !pe.CanAccess(client.role, req.Method) { slog.Warn("permission denied", "method", req.Method, "role", client.role, "client", client.id) slog.Warn("security.permission_denied", "method", req.Method, "role", client.role, "required", string(required), "client", client.id) } } ``` ### 默认拒绝策略 (policy.go) ```go // 修复前:未分类方法返回RoleNone(允许访问) // 修复后:未分类方法返回RoleNone(拒绝访问) // RoleNone is a sentinel returned by MethodRole for methods that have no // explicit classification. The router treats it as deny-for-everyone so // newly-added RPCs are secure-by-default (fail-closed). RoleNone Role = "" ``` ### 方法分类 (policy.go) ```go // 管理员方法 func isAdminMethod(method string) bool { adminMethods := []string{ protocol.MethodConfigApply, protocol.MethodConfigPatch, protocol.MethodConfigSchema, protocol.MethodConfigPermissionsList, protocol.MethodConfigPermissionsGrant, protocol.MethodConfigPermissionsRevoke, protocol.MethodAgentsCreate, protocol.MethodAgentsUpdate, protocol.MethodAgentsDelete, protocol.MethodAgentsLinkList, protocol.MethodAgentsLinkCreate, protocol.MethodAgentsLinkUpdate, protocol.MethodAgentsLinkDelete, protocol.MethodChannelsToggle, protocol.MethodChannelInstancesCreate, protocol.MethodChannelInstancesUpdate, protocol.MethodChannelInstancesDelete, protocol.MethodPairingApprove, protocol.MethodPairingDeny, protocol.MethodPairingList, protocol.MethodPairingRevoke, protocol.MethodTeamsList, protocol.MethodTeamsCreate, protocol.MethodTeamsDelete, protocol.MethodTeamsTaskList, protocol.MethodTeamsTaskSet, protocol.MethodTeamsTaskComments, protocol.MethodTeamsTaskEvents, protocol.MethodTeamsTaskUpdate, protocol.MethodTeamsMemberAdd, protocol.MethodTeamsMembersRemove, protocol.MethodTeamsTaskDelete, protocol.MethodTeamsTaskDeleteBulk, "tenants.create", "tenants.update", "tenants.users.add", "tenants.users.remove", protocol.MethodAPIKeysList, protocol.MethodAPIKeysCreate, protocol.MethodAPIKeysRevoke, protocol.MethodSkillsUpdate, protocol.MethodHeartbeatSet, protocol.MethodHeartbeatToggle, protocol.MethodHeartbeatTest, protocol.MethodHeartbeatCheckListSet, protocol.MethodLogsTail, protocol.MethodHooksCreate, protocol.MethodHooksUpdate, protocol.MethodHooksDelete, protocol.MethodHooksToggle, protocol.MethodVoicesRefresh, protocol.MethodTTSDisable, protocol.MethodTTSEnable, protocol.MethodTTSSetProvider, } return slices.Contains(adminMethods, method) } ``` ### 写入方法分类 (policy.go) ```go func isWriteMethod(method string) bool { writePrefix := []string{ "pairing.", "device.pair.", "approvals.", "exec.approval.", } writeExact := []string{ protocol.MethodChatSend, protocol.MethodChatAbort, protocol.MethodSessionsDelete, protocol.MethodSessionsReset, protocol.MethodSessionsPatch, protocol.MethodCronCreate, protocol.MethodCronUpdate, protocol.MethodCronDelete, protocol.MethodCronToggle, protocol.MethodCronRun, protocol.MethodSend, protocol.MethodAgentsFileSet, protocol.MethodTeamsTaskApprove, protocol.MethodTeamsTaskReject, protocol.MethodTeamsTaskComment, protocol.MethodTeamsTaskCreate, protocol.MethodTeamsTaskAssign, protocol.MethodTeamsWorkspaceDelete, protocol.MethodHooksTest, protocol.MethodPairingRequest, protocol.MethodApprovalApprove, protocol.MethodApprovalDeny, protocol.MethodTTSConvert, protocol.MethodBrowserAct, } // ... 其他逻辑 } ```