Files
fire-safety-ymd/docs/project/security-access-control-boundary.md
2026-09-07 12:55:43 +08:00

15 KiB
Raw Blame History

安全与访问控制边界

内容
项目 fire-safety-ymd
状态 SuperAgent 出站、用户对话 API v1、兼容 Chat 403 Origin 诊断日志与空间只读 MCP 代码控制已实现;真实环境、最终用户授权和持久审计待完成
最近更新 2026-09-07

1. 目的

本文定义用户侧应用、Go 服务、SuperAgent、MCP 工具和 PostgreSQL/PostGIS 之间的信任边界。当前已实现服务间 MCP 基线控制,但不代表最终用户鉴权、实库 readiness、生产网络或持久审计已经完成。

2. 当前实现状态

能力 状态 当前含义
GET /health 已实现 公开的进程存活响应,不访问业务数据
用户认证 仅首版联调门禁 /api/chat 使用独立静态 Bearer可选兼容路径用同一信任方向的 xtoken;默认要求至少 32 个可打印 ASCII 字符;已交付旧短凭证仅可通过默认关闭的显式 legacy 开关在受控测试/迁移窗口暂时兼容;它们不是最终用户身份,身份提供方和正式 Token 格式待确认
角色/区域/租户授权 部分实现 MCP 使用显式服务端静态范围:默认镇街白名单,或经授权的数据库全范围;最终用户动态身份/角色/租户仍未实现
SuperAgent Open API Adapter 已实现、模拟测试通过 默认关闭Secret 仅由项目环境变量注入;真实环境未联调
用户聊天与会话映射 已实现、默认关闭 原生 POST /api/chat 和可选 DashScope 风格 completion 路径、严格 SSE 最终回答、单进程内存映射和同会话并发冲突;无持久化、跨实例恢复或最终用户授权
MCP 工具 已实现、默认关闭 7 个固定只读工具(含地名候选搜索);独立 Bearer、body/Origin/schema/半径/数量/超时控制;实库严格 readiness 和本地真实工具查询已通过,公网/SuperAgent 待联调
PostgreSQL/PostGIS SRID 严格 readiness 已通过 pgxpool、连接级默认只读、固定参数化 SQL4,048 条非空几何已补齐 SRID 432635 条无效几何继续排除并告警
测试环境容器/公网入口 配置基线已建立、目标机待验证 单实例非 root 容器只发布宿主机回环端口Nginx 终止 TLS 并只公开精确路径;运行时不接收迁移凭证
审计存储 未实现 事件、字段和保留周期待确认

3. 信任区域

区域 信任判断 主要控制
用户输入 不可信 身份认证、大小限制、格式校验、提示注入隔离
Chat 静态 Bearer 仅受控联调可信 独立 Secret、精确 CORS Origin、默认关闭不能充当用户身份或字段级授权
Go 服务端身份上下文 授权事实的唯一入口 Token 验证、角色与区域解析、不可被模型覆盖
SuperAgent 输入与输出 外部、非权威 最少数据、超时、输出约束、工具侧重新授权
MCP 参数 不可信,即使由 Agent 生成 schema 校验、范围上限、服务端注入授权上下文
Repository 查询 受控代码 参数化 SQL、白名单、只读角色、超时和行数上限
PostgreSQL/PostGIS 业务事实来源但可能陈旧或不完整 权限、数据质量、来源与更新时间标记
日志与审计 受限数据面 脱敏、访问控制、保留和完整性策略

4. 身份与授权原则

  • 客户端提交的 user_id、角色、区域或租户声明不能直接作为授权依据。
  • 首版 /api/chat DTO 根本不接受 user_idexternal_subject_id、角色、区域、Provider Session 或 metadata服务端固定测试主体不能用于用户级授权。
  • Go 服务从经过验证的凭证中建立可信身份上下文,并将授权范围与请求生命周期绑定。
  • SuperAgent 不做最终授权;每次 MCP 调用都在服务端重新校验工具权限和数据范围。
  • 模型可提供地点、半径、资源类型等业务查询条件,但不能自由指定要冒充的用户、租户或权限。
  • 默认拒绝;新增工具、字段或数据表必须进入显式权限矩阵。
  • 联系人、电话和精确敏感位置按字段级权限控制,不因同一记录的普通字段可见而自动可见。
  • FIRE_SAFETY_MCP_SCOPE_MODE=all 是 MCP 固定查询表的数据库全范围授权,只能在该 MCP 凭证确实获准读取这些表全部记录时显式使用;它不会从业务数据自动推导授权。
  • all 与镇街白名单不能同时配置,避免操作者误以为白名单仍限制结果;两种模式都不能由模型或工具参数修改。
  • FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN 默认必须为 false。只有已交付旧客户端的 Chat 凭证较短且无法立即更换时,才允许在受控测试/迁移窗口显式设为 true无论开关如何Chat 凭证必须非空、不超过 4096 字节且仅含 ASCII 0x21-0x7e(不得包含空格、控制字符或 Unicode。该开关只影响 Chat 原生 Bearer 和兼容入口 xtoken,不放宽 MCP Token 至少 32 个可打印 ASCII 字符的要求Chat、SuperAgent Open API 和 MCP 三种凭证必须不同。轮换完成后必须恢复 false,新环境不得开启。启用期间仅记录不含 Secret 的安全 warning以提示后续清理。
  • 地名搜索只读取已允许的名称、镇街、村庄和几何字段,仍应用相同服务端数据范围;不读取联系人、电话或任意表。
  • 地名匹配输出属于候选。线和面的代表点只用于帮助用户识别记录,未经用户确认不得作为演练点继续查询或形成距离结论。

5. MCP 工具边界

MCP 第一阶段已按以下只读边界实现:

  • 工具对应稳定领域能力,不提供 execute_sql、任意表查询、文件读取或通用 HTTP 代理。
  • JSON schema 限制字符串长度、枚举、坐标范围、半径、分页和最大结果数。
  • Repository 只访问允许的 schema、表和字段动态标识符来自代码白名单。
  • 输出只包含回答所需字段,并表达数据来源、更新时间、状态和不确定性。
  • 权限不足与“没有数据”使用不同稳定状态,避免 Agent 猜测。
  • 调用记录至少关联 request ID、可信主体、工具名、授权范围、耗时、结果类别和数据源版本审计日志不保存无必要的完整敏感正文。

当前日志已记录 request ID、操作、耗时和结果类别可信主体、数据源版本和持久审计尚未实现因此生产前仍需补齐。兼容 completion 的拒绝路径另有受限诊断字段:非预检请求仅在 Origin 被拒绝时记录 result=forbidden_origin 和实际 OriginCORS 预检拒绝记录 result=preflight_forbidden、实际 Origin、请求方法、请求头名称集合和稳定 reason。 每个请求 Header 最多保留前 256 个输入字节,超长值追加 [truncated] 后再引用/ASCII 转义;成功请求不记录 Origin。日志绝不记录 xtokenAuthorizationCookie、prompt/body、会话 ID 或 Provider 数据。

任何写工具、资源调度或状态变更都需要新的 Spec、幂等设计、人工确认边界、审计和安全 Review不属于默认扩展。

6. PostgreSQL/PostGIS 边界

  • 应用数据库角色使用最小权限MCP 查询角色默认只授予必要表或视图的 SELECT
  • 数据迁移凭证与运行时只读凭证分离2026-09-05 的一次性 SRID 迁移使用表所有者执行,完成后不应保留在应用部署环境。
  • Compose 从未提交的 .env 注入运行时配置,但会把 FIRE_SAFETY_POSTGIS_MIGRATION_DSN 强制覆盖为空;镜像构建不得复制 .env、证书、SQL 导出或 Excel 数据。
  • 应用容器内监听 8080只发布宿主机 127.0.0.1:16587,公网只经 Nginx 的 HTTPS 精确路径进入16587、容器 8080 和数据库端口不得加入公网安全组。
  • 使用参数化查询,不把用户或模型内容拼入 SQL、表名、排序或空间表达式。
  • 设置连接、查询和 statement timeout限制半径、结果数和空间复杂度防止昂贵查询。
  • 优先通过项目领域视图或明确映射屏蔽历史物理表名和不一致字段。
  • 验证 extension、SRID、几何类型、有效性、索引和单位后才开放空间结论。当前无效几何不参与结论原始记录保留readiness 告警,固定查询使用 ST_IsValid 排除,工具响应声明结果可能不完整。
  • 原始 SQL 中的 DROP TABLE、owner、sequence 和 extension 操作必须在隔离环境审计,不对现有数据库直接执行。
  • 数据库错误对外映射为稳定错误类别,不返回 DSN、SQL 文本、schema 细节或堆栈。

7. SuperAgent 数据边界

  • 只发送完成当前请求所需的最少上下文,不默认发送联系人、电话、完整数据库记录或长期会话历史。
  • SuperAgent 凭证仅保存在服务端 Secret按环境隔离并支持轮换。
  • Open API Key 不得复用 TH Hotel 或其他项目凭证;本项目只读取 FIRE_SAFETY_SUPERAGENT_OPEN_API_KEY
  • Provider 的 Content-Location 必须与配置 Base URL 同源;客户端禁止跟随重定向,避免跨域转发 Authorization。
  • SSE 只有在最终内容、成功 run.completed 和顶层 end 同时存在时成功;断流恢复不得重发原消息。
  • 公开 Trace 投影不包含原始工具输入、输出、Header、Cookie 或 Provider 原始 payload。
  • 明确平台的数据留存、训练使用、区域、子处理方和删除能力后,才能发送受限数据。
  • 流式事件和工具调用都按不可信外部数据解析,设置消息大小、事件类型和状态机约束。
  • 上游答案返回用户前应保留安全提示、数据时间和工具错误状态,不能把模型文本升级为权威事实。
  • /api/chat 不转发消息 delta只有 Adapter 的严格成功条件全部满足后才发送最终 messagedone。公开进度只保留安全的 run.* / tool.* 事件、工具名和状态。
  • 兼容 completion 路径同样不转发消息 delta它先返回无正文的 finish_reason="null",严格成功后才发送 finish_reason="stop" 和最终正文。URL App ID 只是公开路由标识,不是授权依据。
  • 兼容路径的 xtoken 复用 Chat 入站凭证而不是 SuperAgent Key。Nginx 不保存、比较或注入任何应用 Secret只把 Header 交给 Go 校验。
  • FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN 是 Go 服务侧的配置,不由 Nginx 参与Nginx 不读取、比较或存储 Chat Token短凭证兼容规则和 warning 均由 Go 执行。
  • 同一内存会话只允许一个活动 Run。流失败或结果不确定时删除本地映射避免继续复用可能仍在运行的 Provider Session。
  • 当前会话只在单进程内存中保存且有数量/TTL 上限;不保存消息历史。重启或多实例切换会导致旧对话不可用,不得向用户承诺持久会话。

8. 敏感数据与日志

至少按以下类别处理:

数据 基线分类 日志策略
API Key、Token、Cookie、数据库密码 Secret 禁止记录
联系人、电话、用户标识 个人/受限 默认脱敏,不记录完整值
精确设施或风险区域坐标 业务敏感 按权限最少披露,不记录完整结果集
用户问题和对话 可能含敏感信息 默认不记录原文,使用摘要或分类字段
工具名、耗时、结果类别 运行元数据 可记录,不附敏感 payload
兼容 Chat 被拒绝的 Origin 诊断字段 受限请求元数据 仅拒绝时记录引用/转义且有界的 Origin预检附方法、请求头名称集合和原因禁止 Secret 与请求正文

生产前需确定数据分类负责人、日志访问角色、保留周期、删除流程和安全事件响应方式。

当前 Chat 日志只记录 request ID、结果类别、是否复用和耗时不记录消息、回答、对话 ID、Provider Session 或 Trace payload。 兼容 completion 的 403 Origin 诊断是例外但仍受严格边界约束:dashscope_chat_requestresult=forbidden_origin 时记录有界的 origin;每个请求 Header 最多保留前 256 个输入字节,超长值追加 [truncated] 后再引用/ASCII 转义。在 result=preflight_forbidden 时再记录同样有界的 preflight_methodpreflight_headers 和稳定 reasonorigin_missingorigin_not_allowedmethod_not_allowedheaders_not_allowed)。 这些字段只用于定位发送方的精确浏览器 Origin不是授权凭证不记录 xtokenAuthorizationCookie、 prompt/body、会话或 Provider 数据。docs/import/db-samples/*.sql 含真实联系人、电话和精确坐标,仅作为本地只读输入并由 Git 忽略;不得执行或进入版本历史。长期测试数据必须另做脱敏 fixture。

8.1 兼容 Chat 403 诊断与 Origin 白名单

本节只适用于 DashScope 风格兼容 completion。收到 HTTP 403 后,运维人员在受限日志中按 request_id 关联响应;result=forbidden_origin 表示普通请求的 Origin 不在白名单,result=preflight_forbidden 还需根据 reason 判断是来源、方法还是请求头名称集合不符合预检契约。日志中的值是经过引用/转义的非可信输入, 不能当作 shell 代码执行;出现 [truncated] 时必须回到浏览器 Network 面板确认完整值。

确认发送方后,将精确的浏览器来源配置到 FIRE_SAFETY_CHAT_ALLOWED_ORIGINS:多个值用英文逗号分隔, 每项的规范形式是 scheme://host[:port],不能带非根路径、查询或片段,也禁止 *;单个末尾 / 会被接受并 规范化移除,配置时建议省略。例如页面来源确实为 http://example.test:9045 时才加入该值;不要把 API 路径、Nginx 上游地址或 xtoken 放入白名单。 修改环境配置后必须重新 build代码变更时并 recreate 运行容器,不能依赖 restart 重新加载配置。

9. 应急场景安全

  • 系统只提供辅助信息,不替代报警、撤离和现场指挥。
  • 数据缺失、陈旧、无状态或工具失败时必须显式说明,不能生成看似确定的资源可用结论。
  • 涉及路线、危险源或处置禁忌的功能需要专门规则、权威来源、版本和测试,不仅依赖模型提示词。
  • 任何可能延误报警或撤离的交互流程都必须在实现前进行安全审查。

10. 待确认决策

  • 身份提供方、Token 验证方式和服务间认证。
  • /api/chat 与兼容 completion 路径从静态联调凭证迁移到真实用户身份的方案,以及共享会话、主动取消、限流和滥用防护。
  • 是否存在多租户;若不存在,区域/组织数据范围如何表达。
  • 角色与字段级权限矩阵,尤其是联系人和精确位置。
  • SuperAgent 的部署方、数据处理条款、鉴权、MCP 回调认证与网络边界。
  • 数据库网络拓扑、只读账号、视图策略和审计能力。
  • 对话、工具调用和安全审计的存储位置与保留周期。
  • 面向真实火情功能的责任主体、免责声明和人工确认机制。

这些问题影响实现方向,应在对应 Feature 的 Definition of Ready 阶段确认,不在代码中自行假设。