Files
fire-safety-ymd/docs/project/security-access-control-boundary.md
2026-09-05 17:36:23 +08:00

136 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 安全与访问控制边界
| 项 | 内容 |
| --- | --- |
| 项目 | `fire-safety-ymd` |
| 状态 | SuperAgent 出站、用户对话 API v1 与空间只读 MCP 代码控制已实现;真实环境、最终用户授权和持久审计待完成 |
| 最近更新 | 2026-09-05 |
## 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/chat` DTO 根本不接受 `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 字节且仅含 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、操作、耗时和结果类别;可信主体、数据源版本和持久审计尚未实现,因此生产前仍需补齐。
任何写工具、资源调度或状态变更都需要新的 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 日志只记录 request ID、结果类别、是否复用和耗时,不记录消息、回答、对话 ID、Provider Session 或 Trace payload。`docs/import/db-samples/*.sql` 含真实联系人、电话和精确坐标,仅作为本地只读输入并由 Git 忽略;不得执行或进入版本历史。长期测试数据必须另做脱敏 fixture。
## 9. 应急场景安全
- 系统只提供辅助信息,不替代报警、撤离和现场指挥。
- 数据缺失、陈旧、无状态或工具失败时必须显式说明,不能生成看似确定的资源可用结论。
- 涉及路线、危险源或处置禁忌的功能需要专门规则、权威来源、版本和测试,不仅依赖模型提示词。
- 任何可能延误报警或撤离的交互流程都必须在实现前进行安全审查。
## 10. 待确认决策
- 身份提供方、Token 验证方式和服务间认证。
- `/api/chat` 与兼容 `completion` 路径从静态联调凭证迁移到真实用户身份的方案,以及共享会话、主动取消、限流和滥用防护。
- 是否存在多租户;若不存在,区域/组织数据范围如何表达。
- 角色与字段级权限矩阵,尤其是联系人和精确位置。
- SuperAgent 的部署方、数据处理条款、鉴权、MCP 回调认证与网络边界。
- 数据库网络拓扑、只读账号、视图策略和审计能力。
- 对话、工具调用和安全审计的存储位置与保留周期。
- 面向真实火情功能的责任主体、免责声明和人工确认机制。
这些问题影响实现方向,应在对应 Feature 的 Definition of Ready 阶段确认,不在代码中自行假设。