15 KiB
安全与访问控制边界
| 项 | 内容 |
|---|---|
| 项目 | 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、连接级默认只读、固定参数化 SQL;4,048 条非空几何已补齐 SRID 4326,35 条无效几何继续排除并告警 |
| 测试环境容器/公网入口 | 配置基线已建立、目标机待验证 | 单实例非 root 容器,只发布宿主机回环端口;Nginx 终止 TLS 并只公开精确路径;运行时不接收迁移凭证 |
| 审计存储 | 未实现 | 事件、字段和保留周期待确认 |
3. 信任区域
| 区域 | 信任判断 | 主要控制 |
|---|---|---|
| 用户输入 | 不可信 | 身份认证、大小限制、格式校验、提示注入隔离 |
| Chat 静态 Bearer | 仅受控联调可信 | 独立 Secret、精确 CORS Origin、默认关闭;不能充当用户身份或字段级授权 |
| Go 服务端身份上下文 | 授权事实的唯一入口 | Token 验证、角色与区域解析、不可被模型覆盖 |
| SuperAgent 输入与输出 | 外部、非权威 | 最少数据、超时、输出约束、工具侧重新授权 |
| MCP 参数 | 不可信,即使由 Agent 生成 | schema 校验、范围上限、服务端注入授权上下文 |
| Repository 查询 | 受控代码 | 参数化 SQL、白名单、只读角色、超时和行数上限 |
| PostgreSQL/PostGIS | 业务事实来源但可能陈旧或不完整 | 权限、数据质量、来源与更新时间标记 |
| 日志与审计 | 受限数据面 | 脱敏、访问控制、保留和完整性策略 |
4. 身份与授权原则
- 客户端提交的
user_id、角色、区域或租户声明不能直接作为授权依据。 - 首版
/api/chatDTO 根本不接受user_id、external_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 字节且仅含 ASCII0x21-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 和实际 Origin;CORS 预检拒绝记录
result=preflight_forbidden、实际 Origin、请求方法、请求头名称集合和稳定 reason。
每个请求 Header 最多保留前 256 个输入字节,超长值追加 [truncated] 后再引用/ASCII 转义;成功请求不记录
Origin。日志绝不记录 xtoken、Authorization、Cookie、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 的严格成功条件全部满足后才发送最终message和done。公开进度只保留安全的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_request 在
result=forbidden_origin 时记录有界的 origin;每个请求 Header 最多保留前 256 个输入字节,超长值追加
[truncated] 后再引用/ASCII 转义。在
result=preflight_forbidden 时再记录同样有界的 preflight_method、preflight_headers 和稳定
reason(origin_missing、origin_not_allowed、method_not_allowed 或 headers_not_allowed)。
这些字段只用于定位发送方的精确浏览器 Origin,不是授权凭证;不记录 xtoken、Authorization、Cookie、
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 阶段确认,不在代码中自行假设。