# 05 · 安全审查与验收
## 1. 威胁建模(按 STRIDE 摘要)
| 威胁 | 攻击面 | 缓解 |
|---|---|---|
| **S** 身份伪造 | PHP 后台登录 | bcrypt + 单管理员 + 登录限速(5 次锁 10 分钟)+ session 再生 |
| **T** 篡改清单 | 后台 CRUD / API 输出 | CSRF token、预编译语句、输出只读 API 无副作用 |
| **R** 抵赖 | 后台操作 | v1 单管理员场景不做审计日志(记录为已知限制),操作留 SQLite updated_at |
| **I** 信息泄露 | API/日志/前端 | 公开 API 只含站长愿意公开的模型元数据;插件不采集 Key;日志脱敏 |
| **D** 拒绝服务 | 公开 API / 登录 | page/page_size 钳制、no-store、登录限速;SQLite 单文件读放大有限,文档建议生产环境加 CDN/限流 WAF |
| **E** 提权 | include/eval/upload | 无文件上传、无变量 include、无 eval;PHP data 目录 deny-all |
## 2. 插件侧安全要求
- [x] 用户 API Key 全程不经本插件任何一端(设计性保证,见 02 §8)
- [x] 一键配置仅写入官方 settings 文档的 `llm-pi-ai.providers` 子树,不触碰其他命名空间
- [x] 外链 `noopener noreferrer`,防 opener 泄漏与 referrer 发送
- [x] 面板渲染的所有远端字符串走安全 DOM API,杜绝存储型 XSS(数据源是站长后台,属半可信)
- [x] 卸载可逆:DOM/样式/注册全部随 Cordis effect 回收
- [x] 不请求 DSH 凭据库、不读取 `.credentials.yaml`
## 3. 安全验收测试矩阵
| # | 用例 | 步骤 | 通过标准 |
|---|---|---|---|
| S-01 | SQL 注入 | 后台标题填 `' ; DROP TABLE models;--` 并保存 | 正常入库显示,表结构无损 |
| S-02 | XSS 存储型 | 标题填 `` / `
` | 字面量展示,无脚本执行(前端+后台双查) |
| S-03 | CSRF | 用外部页面直接 POST admin/models.php | 403 拒绝 |
| S-04 | 越权 | 未登录 GET/POST admin/*.php | 302 到登录页;API 不泄漏管理员路径外信息 |
| S-05 | 暴力破解 | 连错 5 次 | 第 6 次锁定提示,10 分钟后恢复 |
| S-06 | DB 直达 | 浏览器访问 data/*.db | 403/404,不可下载 |
| S-07 | CORS 越权 | 非 白名单 Origin 请求 api/models.php | 不回 ACAO 头,浏览器拦截 |
| S-08 | 分页滥用 | page=-5, 0, 99999, page_size=10^9 | 钳制返回合法结构,500 不出现 |
| S-09 | 协议白名单 | key_apply_url 填 `javascript:alert(1)` | 前端置灰不可点 |
| S-10 | Key 零接触 | 全链路抓包 + 代码 grep | 无任何 Key 字段经过插件前后端 |
| S-11 | 技术栈隐匿 | 直连 `/admin/models.php`、`/includes/config.php`、`/data/x.db`;检查响应头 | `.php` 一律 301 到干净路由或 404;敏感路径 404;无 `X-Powered-By`/目录列表/堆栈泄漏 |
| S-12 | 干净 URL 回归 | 走通 `/`、`/api/models`、`/admin` 全流程 | 页面/接口全部以无后缀 URL 工作,功能与旧路径等价 |
## 4. 功能验收矩阵(产品层 DoD)
| # | 用例 | 通过标准 |
|---|---|---|
| F-01 | 安装 | `dsh plugin add github:/dsh-free-models-hub` 一条命令完成,无需 allowBuilds |
| F-02 | 面板出现 | 左栏可见「免费模型榜」,样式与原生协调 |
| F-03 | 分页 | 25 条数据 → 3 页;页码窗口正确;首末页按钮禁用态正确;点页码直达 |
| F-04 | 展开 | 点击标题展开 API 地址/模型名/申请密钥按钮/一键配置按钮;再点收起 |
| F-05 | 申请密钥 | 新标签打开注册链接,`noopener` 生效 |
| F-06 | 一键配置 | Settings → Models 出现 `freehub-*` 自定义提供方,baseURL/协议/模型名正确 |
| F-07 | Key 收尾 | 用户在设置页粘贴 Key 后,该模型出现在模型选择器并可发起会话 |
| F-08 | 兜底弹窗 | 屏蔽 settingsScope 写入路径时弹出 YAML 引导且复制可用 |
| F-09 | 底部菜单 | 三项菜单文案链接正确,新标签打开 |
| F-10 | 升级 | 市场"更新"到新版本后面板正常,配置保留 |
| F-11 | 卸载 | remove 后面板消失、无残留节点/样式、dump-config 无残留层 |
| F-12 | 异常态 | 后端宕机/超时/坏 JSON → 错误态 + 重试按钮,DSH 其余功能不受影响 |
## 5. 性能与非功能
- API 响应 P95 < 300ms(百条级数据);面板首屏渲染 < 100ms(10 条)。
- client bundle ≤ 60KB(未压缩);零运行时 npm 依赖。
- 无障碍:列表行可聚焦、aria-expanded、按钮语义化标签。
## 6. 签收流程
1. 开发自测 → 按 §3/§4 逐条执行并在本文档附录勾选留档。
2. 双审查(04 清单 + 本文档 §2)通过后合入 main。
3. 打 tag 前 CI 必须 green(lint/test/build 同步校验)。
4. 发布后在真实 dsh web 环境(Node LTS + Chromium)复验 F-01/F-02/F-06 三项冒烟。
---
## 附录 A · v0.1.0 验收执行记录
执行环境:Windows + PHP 7.4.3(cli,pdo_sqlite)+ Node 24 · 日期 2026-08-26
| 用例 | 结果 | 说明 |
|---|---|---|
| S-01 SQL 注入 | ☑ 通过 | 端到端:恶意标题入库为纯文本,表结构无损(PDO 预编译) |
| S-02 XSS 存储型 | ☑ 通过 | 端到端:`` 后台页转义输出;公开榜单页同样转义;插件端 textContent 渲染 |
| S-03 CSRF | ☑ 通过 | 真实 Apache+FCGID 环境复现:无 token 直 POST 走错误分支;带 token 全流程通过 |
| S-04 越权 | ☑ 通过 | 未登录访问 admin/* 被 fmh_require_admin 弹回登录页 |
| S-05 暴力破解 | ☑ 通过(逻辑) | 5 次失败锁 600s;成功清零;窗口过期自动重置 |
| S-06 DB 直达 | ☑ 通过 | 端到端 `/data/*.db` → 404;.htaccess Require all denied + 随机库文件名 + 安装密钥 0600 |
| S-07 CORS 越权 | ☑ 通过 | 端到端:白名单来源精确回显 ACAO,evil 来源无允许头;OPTIONS 204 |
| S-08 分页滥用 | ☑ 通过 | page/page_size 服务端钳制(get_int min/max);越界页回落末页 |
| S-09 协议白名单 | ☑ 通过 | 端到端:key_apply_url 填 javascript: 出口置空;前端 safeHttpUrl 仅 http(s)(单测覆盖) |
| S-10 Key 零接触 | ☑ 通过 | 代码走查:插件两端无任何 key 字段读写/传输;Key 只走官方 write-only 凭据通道 |
| S-11 技术栈隐匿 | ☑ 通过 | 端到端:`/admin/models.php`→301 干净路由;`/includes/config.php`→404;`/data/x.db`→404;响应无 X-Powered-By;未知 `.php`(含 diag.php)→404 |
| S-12 干净 URL 回归 | ☑ 通过 | 端到端:`/`(榜单)、`/api/models`(JSON+CORS)、`/admin/`(登录→强制改密→CRUD) 全流程在干净路由下通过 |
| S-13 会话持久化(真实宿主) | ☑ 通过 | phpStudy Apache2.4.39+FCGID+PHP7.4.3 实测:登录→302→受保护页会话保持(修复 mod_dir 对 `/admin` 目录的 301 导致 POST 降级问题后) |
| S-14 隧道链路 | ☑ 通过 | 经 Cloudflare Tunnel(free-api.gd7.cn→127.0.0.1:38383)完整登录流程通过;Cookie 域属性正确 |
| F-02/F-03 数据闭环 | ☑ 通过 | CRUD 2 条 → API 分页 total=2/pages=2、sort ASC 排序正确 |
| F-06 写入格式 | ☑ 通过(单测) | buildProviderEntry 产出与官方 llm-pi-ai 路由字段一致;YAML 片段含安全引号转义 |
| F-08 兜底弹窗 | ☑ 通过(单测+走查) | toYamlSnippet 快照断言;settingsScope 全程 try/catch 降级 |
| F-01 安装契约 | ☑ 通过 | npm pack 产物最小化;零依赖离线安装 590ms;安装器激活契约 11/11 PASS(scripts/installer-contract.cjs) |
| F-10 更新路径 | ☑ 通过 | 0.1.0→0.1.1 覆盖安装 454ms,更新后契约 11/11 PASS(对应市场"更新安装"= 重跑 add 拉新 tag) |
| F-02/F-04/F-05/F-07/F-09/F-11/F-12 | ☐ 待真机 | 需在真实 dsh web 环境按矩阵执行(本机网络无法拉取 DSH 运行时包体;安装契约已逐项验证) |
> 结论:代码级与后端级验收全部通过;F 组剩余项依赖真实 DSH 宿主,发布前按 §6 流程冒烟
> (安装出现面板 / 一键配置写入 / 卸载可逆)。