# 缺陷单:敏感词/隐私正则/审计日志/命中配置 13 端点无鉴权(v1.1 实施漏加 require_admin) > **缺陷编号**: BUG-通用-004 > **版本**: v1.0 > **状态**: [待修复] > **优先级**: P0-Critical(合规/安全) > **发现日期**: 2026-08-05 > **发现人**: 宋献 > **指派人**: 宋献 > **修复人**: Duckula (AI助手) > **关联需求**: REQ-通用-004(敏感词检测) > **关联文档**: > - PRD: `docs/01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md`(v1.2 待出) > - 技术方案: `docs/02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md`(v1.2 待出) > - 任务说明书: `docs/07-项目管理/任务说明书/任务说明书-03-v1.1-敏感词词库入库+后台UI.md`(v1.1 增量,即将归档为 .v1.1.archive.md) > - 源码: `src/backend/app/api/admin/sensitive_words.py` > - 测试用例: `docs/03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md` > - 整改记录: `docs/04-运维文档/部署运维/00-文档规范化整改记录.md`(#5 整改记录) --- ## 1. 基本信息 | 字段 | 内容 | |------|------| | 缺陷标题 | `src/backend/app/api/admin/sensitive_words.py` 13 个端点全部未挂 `Depends(require_admin)`,任何能访问 `http://10.90.5.110:8000/api/admin/...` 的内部用户均可直接调用,等同 admin 权限裸奔 | | 影响范围 | 13 端点全覆盖:敏感词 CRUD/测试/重载 + 隐私正则 CRUD/测试 + 审计日志列表/统计 + 命中动作配置 | | 涉及模块 | 后端 API(backend FastAPI, v1.1 新增路由) | | 涉及文件 | `src/backend/app/api/admin/sensitive_words.py`(仅此 1 个文件,APIRouter 未声明 `dependencies=`) | | 触发条件 | 任意已登录用户(含坐席/普通员工)通过任何渠道获取 `Bearer token` 后直接调用这 13 个端点;或绕开前端直接 curl 后端 | | 预期行为 | 调用 13 端点时若 `agent.role != "admin"`,应抛出 AppException(1004, "无管理权限")(与 `admin_api.py` 行为一致) | | 实际行为 | 13 端点全部 200 OK 通过;任何 token 持有者拥有增删改查敏感词库、读取审计日志(含员工消息片段)、上传任意正则(可触发 ReDoS)、触发 `/sensitive-words/reload` 强制热加载词库的能力 | ### 1.1 端点清单(v1.1 实施,13 个) | 类别 | 方法 | 路径 | 用途 | |---|---|---|---| | 敏感词 | GET | `/api/admin/sensitive-words` | 词库列表(分页+筛选) | | 敏感词 | POST | `/api/admin/sensitive-words` | 新增词 | | 敏感词 | PUT | `/api/admin/sensitive-words/{id}` | 更新词 | | 敏感词 | DELETE | `/api/admin/sensitive-words/{id}` | 删除词(软删) | | 敏感词 | POST | `/api/admin/sensitive-words/test` | 测试输入文本 | | 敏感词 | POST | `/api/admin/sensitive-words/reload` | 强制从 DB 热加载词库 | | 隐私正则 | GET | `/api/admin/privacy-patterns` | 列表 | | 隐私正则 | POST | `/api/admin/privacy-patterns` | 新增 | | 隐私正则 | PUT | `/api/admin/privacy-patterns/{id}` | 更新 | | 隐私正则 | POST | `/api/admin/privacy-patterns/{id}/test` | 正则测试器(ReDoS 入口) | | 审计日志 | GET | `/api/admin/moderation-logs` | 列表(含 message_id/agent_id/matched_words/text_excerpt) | | 审计日志 | GET | `/api/admin/moderation-logs/stats` | 统计 | | 命中配置 | GET | `/api/admin/moderation-config` | 全局命中动作配置 | --- ## 2. 复现步骤 ### 2.1 复现 1:词库列表裸奔(GET) ```bash # 任意坐席账号(role=agent,非 admin)的 Bearer token TOKEN="<任意普通坐席 token>" curl -sS -X GET "http://10.90.5.110:8000/api/admin/sensitive-words?page=1&page_size=20" \ -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" ``` **预期**:HTTP 401/403,返回 `{"code": 1004, "message": "无管理权限"}` **实际**:HTTP 200,返回完整词库(含 profanity/politics/porn 等所有分类与 severity) ### 2.2 复现 2:审计日志裸奔(GET) ```bash curl -sS -X GET "http://10.90.5.110:8000/api/admin/moderation-logs?page=1&page_size=20" \ -H "Authorization: Bearer $TOKEN" ``` **预期**:HTTP 401/403 **实际**:HTTP 200,返回 `[{agent_id, matched_words, category, action, text_excerpt, created_at}, ...]`,含员工与坐席对话内容片段 ### 2.3 复现 3:词库篡改(POST/PUT/DELETE) ```bash # 任意 token 即可删除核心拦截词 curl -sS -X DELETE "http://10.90.5.110:8000/api/admin/sensitive-words/1" \ -H "Authorization: Bearer $TOKEN" # 实际:HTTP 200,is_active=false,词条立即全局失效 ``` ### 2.4 复现 4:正则测试器 ReDoS(POST) ```bash curl -sS -X POST "http://10.90.5.110:8000/api/admin/privacy-patterns/1/test" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"pattern": "(a+)+$", "text": "aaaaaaaaaaaaaaaaaaaab"}' # 实际:HTTP 200,触发 catastrophic backtracking,CPU 100% 数秒 ``` ### 2.5 复现 5:热加载触发(POST /reload) ```bash curl -sS -X POST "http://10.90.5.110:8000/api/admin/sensitive-words/reload" \ -H "Authorization: Bearer $TOKEN" # 实际:HTTP 200,全量重新加载词库到内存;高频调用可拖垮 DB + 后端 ``` --- ## 3. 根因分析 ### 3.1 实现层:APIRouter 未声明 `dependencies` `sensitive_words.py:44` 创建 router 时未附加 `dependencies=[Depends(require_admin)]`: ```python # ❌ 错误(当前实现) router = APIRouter(prefix="/admin", tags=["敏感词管理(v1.1)"]) # ✅ 正确(应改为) from app.api.admin_api import require_admin router = APIRouter( prefix="/admin", tags=["敏感词管理(v1.1)"], dependencies=[Depends(require_admin)], # 一行全覆盖 13 端点 ) ``` ### 3.2 对比层:同类 admin 路由全部正确 | 文件 | require_admin 覆盖 | |---|---| | `admin_api.py` | ✅ 每个端点 `Depends(require_admin)` | | `admin_roles.py` | ✅ 已加 | | `admin_users.py` | ✅ 已加 | | `welcome.py` | ✅ 已加 | | `quiz_admin.py` | ✅ 已加 | | `troubleshooting_templates.py` | ✅ 已加 | | **`admin/sensitive_words.py`** | ❌ **13 端点全部漏挂** | ### 3.3 规范层:v1.1 实施未走 spec.md 强制约束 - 技术方案 v1.0 §6.3 路由层表格**已明确列出**所有 11 个端点的"权限:admin" - PRD v1.0 §5.2 路由层 API 计划清单同样声明 admin 权限 - v1.1 实施时(任务说明书 v1.1)未对照规范逐项实现 - 任务说明书也未在验收用例里写"13 端点必须 401" → 测试环节也漏了 ### 3.4 三层根因(缺一不可) | 层 | 问题 | 体现 | |---|---|---| | 规范 | v1.1 任务说明书验收清单缺"鉴权用例" | §5 输出成果要求未列鉴权维度 | | 实现 | APIRouter 未声明 `dependencies` | sensitive_words.py:44 | | 测试 | TC-通用-004 无鉴权章节 | 31 用例全在功能维度,无安全维度 | --- ## 4. 修复方案 ### 4.1 最小修复(一行代码) 修改 `src/backend/app/api/admin/sensitive_words.py:44`: ```python # 顶部 imports 增加 from app.api.admin_api import require_admin # router 创建增加 dependencies router = APIRouter( prefix="/admin", tags=["敏感词管理(v1.1)"], dependencies=[Depends(require_admin)], # 13 端点全覆盖 ) ``` ### 4.2 文档同步(按 product-doc-standard 铁律) | 文档 | 动作 | |---|---| | PRD v1.0 | 升级到 v1.2,加 §11 v1.1 增量 + §12 v1.2 安全补漏(鉴权) | | 技术方案 v1.0 | 升级到 v1.2,§6.3 路由层表格的"权限"列追加"`+ ` 鉴权依赖:`Depends(require_admin)` | | 任务说明书 v1.1 | 旧名 `.v1.1.archive.md` 归档;新建 v1.2 覆盖鉴权补漏 | | TC-通用-004 | 末尾追加 §10 鉴权用例(4 条) | | 整改记录 | 在 `00-文档规范化整改记录.md` 追加 #5 整改条目 | | Bug 单 | 本文件 | ### 4.3 代码变更清单 | 文件 | 变更类型 | 内容 | |---|---|---| | `src/backend/app/api/admin/sensitive_words.py` | 修改 | imports 增加 `require_admin`;router 加 `dependencies=[Depends(require_admin)]` | | `src/backend/tests/` | 新增 | `test_sensitive_words_auth.py`(鉴权测试 6 条) | --- ## 5. 验证方式 ### 5.1 单元/集成测试(自动化) | 用例 | 预期 | |---|---| | 无 token 调用 13 端点 | 401 | | 普通坐席(role=agent)调用 13 端点 | 403 + `code:1004 无管理权限` | | 管理员(role=admin)调用 13 端点 | 200 | | 重载/正则测试等写操作管理员调用 | 200 | | 修复后源码 grep `Depends(require_admin)` | 应在 sensitive_words.py 出现至少 1 次 | ### 5.2 容器内端到端验证(修复后必做) 1. `docker compose restart backend` 2. `docker compose exec backend grep -n "require_admin" app/api/admin/sensitive_words.py` 3. 用普通坐席 token curl 13 端点 → 应全部 401/403 4. 用 admin token curl 13 端点 → 应全部 200 ### 5.3 回归测试 - v1.1 既有 11 个功能测试用例全部通过 - TC-通用-004 §10 新增鉴权用例 4 条全部通过 --- ## 6. 关联 | 类型 | 文档 | |---|---| | 规范 | `docs/00-产品开发流程与文档管理规范.md`(v1.9)§ 4.3 文档更新时机 + § 11 文档整改实践 | | 需求 | REQ-通用-004 敏感词检测 | | 上游实现 | `src/backend/app/services/content_moderation_service.py`(v1.0 已上线审核服务) | | 上游实现 | `src/backend/app/services/admin/sensitive_word_service.py`(v1.1 词库入库 + 管理 service) | | 数据库迁移 | `src/backend/alembic/versions/056_add_moderation_tables.py` | | 初始化数据 | `src/backend/scripts/init_moderation.sql` | | 测试基线 | `src/backend/tests/test_content_moderation.py`(13 用例基线) | | 对照 admin 路由 | `src/backend/app/api/admin_api.py:50` require_admin 定义 | --- ## 7. 变更记录 | 日期 | 版本 | 变更内容 | 变更人 | 变更原因 | 影响范围 | |------|------|----------|---------|----------|----------| | 2026-08-05 | v1.0 | 首次登记:13 端点无鉴权缺陷 + 修复方案 + 文档同步清单 | 宋献 / Duckula | v1.1 实施时漏加 require_admin 依赖,违反 PRD v1.0 §5.2 + 技术方案 v1.0 §6.3 admin 权限约束 | 13 端点全覆盖;后端 FastAPI 安全维度补漏;PRD/技术方案/任务说明书升级 v1.2 |