security-findings.md 3.9 KB

现网安全发现报告(xlyAi / saas ERP)

调查时间 2026-07-21。对象:saas-8s+ ERP 后端(xlyEntry)+ xlyAi 现有 NL2SQL。 结论:后端不是权限权威,且现有 AI 取数路径存在越权与"读接口可写"风险。下述为供业务方决策的整改清单。

摘要(按严重度)

# 严重度 发现 影响
1 后端逐用户表单/菜单权限被故意关闭 任何登录用户经 API 可碰到 UI 里看不到的表单、可审核/删除全公司数据
2 通用读接口可被当写接口用 bUpdate=1 等参数经参数绑定触发存储过程改库存
3 现有 NL2SQL 无租户过滤、可跨品牌 A 品牌用户"查所有客户"能拿到全部品牌数据
4 NL2SQL 执行期无表白名单、SQL 安全仅部分 构造问题可 SELECT 任意视图;INTO OUTFILE/LOAD_FILE 未挡
5 NL2SQL 流式缓存命中跳过校验、缓存无租户隔离 缓存投毒 / 越权复用

明细

1. 后端不是权限权威(表单级权限被关)

  • AuthorizationInterceptor 里逐用户表单权限校验 checkByUser(约 143–164 行)被注释,注释写着「朱总说不用放 20230626」。
  • getBusinessDataByFormcustomId所有写/动作端点(add/update/delete/审核 doExamine)只挂 @Authorization仅验登录
  • 后端只强制:公司级租户隔离(sBrandsId/sSubsidiaryId)+ 行级 jurisdiction 数据范围。
  • 逐用户表单/菜单权限只在前端 UIsAuthsId 过滤菜单。agent/脚本直接打 API 即绕过 UI → 越权放大。
  • 说明:sAuthsId 的数据仍在(sysjurisdiction,由 getsAuthsIdNew 运行时算出,前端 /getMenuList 用),所以可在 agent 侧补回表单级白名单(xlyAi 已按此实现授权层,见 docs/agent-architecture.md §7)。

2. 读接口可写

  • 通用表单接口把请求体任意 key 按名绑定到存储过程 IN 参、无白名单
  • 例:材料库存台账SP_Inventory_InOutWarehouse,AI 已暴露)在 bUpdate=1真改 MitMaterialsStore/EleMaterialsStock(重算并持久化库存)。
  • 缓解:xlyAi 的 Read 工具只传分页/过滤参数,绝不透传 bUpdate/bUpdateAll(已实现)。根因需后端加参数白名单

3–5. 现有 NL2SQL(XlyErpService.getDynamicTableSqlExec 链路)

  • 无租户过滤:prompt 从不提品牌,代码不追加 WHERE,视图把品牌当普通列但不过滤;规则默认「返回全部数据」→ 跨品牌泄漏。
  • 越权双轴:跨品牌(同上);跨表——工具权限只筛「喂给模型的表」,执行期无 allowlist,构造问题可查任意视图;系统管理员拿到全部工具。
  • SQL 安全仅部分:jsqlparser 挡了 Insert/Update/Delete/Drop + 关键字黑名单,但无正向 SELECT-onlyINTO OUTFILE 未挡、LOAD_FILE 未挡(\bLOAD\bLOAD_FILE)、无 LIMIT。本地账号 root 全权含 FILE。
  • 流式缓存路径命中即跳过校验if isEmpty(cleanSql) 兜底)+ 缓存无租户维度。

建议整改(优先级)

  1. 后端:重开逐用户表单权限校验(checkByUser),或至少对 AI 服务账号强制表单白名单。
  2. 后端:通用读接口加参数白名单,禁止读路径接受写类参数(bUpdate 等)。
  3. NL2SQL(新 queryData 已按此实现):强制单条 SELECT、挡 OUTFILE/LOAD_FILE/系统库、强制 LIMIT、专用只读账号、AST 注入租户谓词、缓存过校验且按品牌隔离。
  4. 金融类数据:写操作/SQL 不可变审计(xlyAi 已建 ai_audit_log)。

注:xlyAi 侧已实现的缓解(授权层白名单、Read 参数白名单、queryData 安全栈、审计)是纵深防御,不替代后端整改(第 1、2 项根因在后端)。