# 管理员恢复安全补充验收 ## 范围与结论 本项证据使用服务器及本机的北京时间,记录日期为 2026 年 9 月 21 日。修改前基线为 `9e5496d`,已提交并推送;不覆盖 `docs/云端全项目测试报告.md` 的用户原有修改。 **Linux 管理员恢复专项及新候选的三库隔离回归通过,不代表整个项目可生产放行。** 正式实例没有升级。本次测试不开放公网恢复接口,不停止 Scert、共享数据库或其他项目。 ## 需求与任务状态 - [✓] 保持受保护本地 IPC、调用者身份和安全凭据校验。 - [✓] 显式验证整数转换边界,保留合法的负值终端设备编码。 - [✓] 管理员流程不从 `PATH` 查找密码代理和 `systemctl`。 - [✓] 云端完整管理员恢复包、真实伪终端、取消及不回显回归。 - [✓] 重建前端、三个插件、Linux 主程序及管理员工具,再运行三库空数据验收。 - [✓] 保留失败记录,停止隔离单元,确认正式服务及二进制未变。 - [✓] 全项目 Linux 目标平台静态安全复扫完成并复核原始输出;仍有 38 条告警,扫描未通过。 - [!] 独立浏览器启动受到执行策略阻止,未取得真实浏览器流程证据。 - [ ] 显式 `systemctl start smail-admin` 的管理员终端完整正向交互仍需单独验收。 - [!] Windows 提权 IPC、凭据轮换、自动续期、外部收件等原有生产门禁仍未关闭。 ## 应用改动 1. Socket 所有者检查改为先拒绝无效有效 UID,再进行宽类型比较,避免将当前 UID 缩窄后比较。 2. 终端设备号先限制在有符号 32 位范围,再显式还原其无符号编码并检查转换边界。不能直接拒绝所有负值,否则会破坏合法高位设备号。 3. `poll` 前验证文件描述符不超过有符号 32 位上界,已关闭文件的无效描述符立即拒绝。 4. 密码代理固定为 `/usr/bin/systemd-ask-password`,服务任务查询固定为 `/usr/bin/systemctl`。环境诊断也检查同一代理路径,不再从可变 `PATH` 找到另一程序。 5. 两处终端关闭显式记录忽略返回值的意图:授权失败仍拒绝;正常退出先取消并等待监视器结束,再关闭无业务写入缓冲的终端描述符。这不是修复两个已确认漏洞,也没有新增扫描忽略注释。 云服务器上的两个系统程序均属于 root,权限为 `0755`。固定路径是该 Linux/systemd 部署的依赖;其他发行版路径不同时不能无条件声称兼容。 ## 专项测试证据 新增 `backend/services/adminreset/numeric_security_linux_test.go`,验证设备号合法边界及四个越界值、描述符转换与关闭状态、恶意 `PATH` 反例和参数原样传递。PATH 测试验证命令构造,不执行伪造程序,也不触发共享系统密码提示。 最终 Linux 测试程序 SHA-256: ```text e18337acaec6c34beddd5ca9b0620e673ee5734568727562977a45bde8858fc2 ``` 云端目录:`/var/tmp/smail-review-security-20260921/20260921-034451/`。 完整管理员恢复包 **41 个顶层测试通过,零跳过,退出码零**。真实 PTY 测试包括隐藏密码输入、可见输入、取消及 EOF 后恢复终端状态;本地 Socket、恢复码、协议撤销等使用隔离数据,不依赖正式数据库。 执行身份为非特权 `smail`,私有网络、私有临时目录、正式数据目录不可访问;内存上限 256 MiB、禁用交换空间、CPU 配额 50%、运行时间上限 300 秒。systemd 输出记录约 43.6 秒、内存峰值 134.3 MiB、交换为零。测试结束后查询单元的 `MemoryPeak` 已为未设置,峰值引用的是执行输出,不伪造持久属性值,也不作为生产容量结论。 本地 Windows 共享代码竞态回归 `node test/run.mjs backend -race -count=1 -p 1 ./services/adminreset` 通过,日志为 `test-log/run-2026-09-20T19-43-45-928Z-19356/`;UTC 文件名与北京时间日期不同。该结果不能替代 Windows 提权 IPC 或 Linux 平台文件的执行。 新编译管理员工具还在云端直接执行了三个边界检查:无参数非正式调用退出 1,密码参数入口退出 2,版本查询退出 0。没有发起真实重置,不能作为正式管理员交互成功的证据。 ## 完整候选与三库回归 版本为 `review-admin-20260921`。构建时仍处于已审查工作区,内嵌 Git 标识为基线加 `-dirty`,不把它冒充已发布版本。构建记录、应用包摘要和源码逐文件摘要分别保存在 `build-admin-hardened.log`、`admin-build-manifest.json`、`admin-source-manifest.json`。 | 产物 | SHA-256 | | --- | --- | | 主程序 | `ef9d5c20fb2c66b1d499feac9f0b7dc72e51f235366f968e7c9b6ceb05cfb22e` | | 管理员工具 | `172ffd558abbbafd0b4fa35c30062ff0fc6d3c5794723061423c4755a2718e48` | 候选位于 `/var/tmp/smail-review-admin-20260921/`。三轮均从新的空数据实例开始,施加 192 MiB 内存上限,使用同一主程序摘要。 | 数据库 | 云端隔离目录后缀 | 结果 | | --- | --- | --- | | SQLite | `smail-review-runtime-20260921-034818` | 通过;仅一个 `smail.db`,完整性检查正常 | | MySQL | `smail-review-runtime-20260921-035045` | 通过;配置历史与业务同库,SQLite 文件零个 | | PostgreSQL | `smail-review-runtime-20260921-035229` | 通过;配置历史与业务同库,SQLite 文件零个 | 复验包括初始化、HTTP 会话撤销、协议码及客户端码撤销后的旧连接拒绝、中文邮件与附件读取、IMAP APPEND/EXPUNGE、过载恢复。所有正式服务的 PID、启动标识和正式主程序摘要保持不变;清理及资源采集错误为空;SQL 测试账号已锁定。本轮是直接 TLS 拓扑,不扩大为新候选的 Nginx 正向复验或跨库切换重启验收。 本地证据位于 `test-log/production-remediation-20260921/`: - `admin-cloud-v2-result.json`、`admin-cloud-v2.log`。 - `admin-cli-denial-result.json`。 - `admin-runtime-results.json`、`admin-runtime-retry-results.json`。 - `admin-runtime-evidence/` 下的逐轮结果、探针及服务日志。 - `admin-evidence-index.json`:三个通过目录和一个失败目录,共十四份文件的摘要均已复核。 - `admin-live-final.json`:正式服务及其他五个保护服务的进程标识、正式程序摘要及隔离单元收尾记录。 ## 静态扫描边界 最终包级扫描使用原有 `gosec v2.22.10`,Windows 主机设置 Linux/amd64、禁用 CGO;不是云端原生扫描。管理员恢复包剩余三条:两条 `/proc` 动态读取 G304 和一条控制目录权限 G302,退出码仍为 1。未新增 `nosec`、未降低等级、未改变失败退出码。 相对原包的九条,三条 G115、一条 G204 和两条 G104 已不再出现。不能把“减少六条告警”表述为修复六个可利用漏洞。初次改法曾产生两个新的转换告警和一个系统工具路径常量的疑似密钥告警;保留 `gosec-admin-numeric.log`,随后改用显式转换边界及准确的代理路径命名,最终记录为 `gosec-admin-numeric-v2.log`。JSON 均从完整日志提取,不算再次扫描。 全项目复扫已经完成:345 个文件、56,823 行,告警 38 条,其中高 2、中 36;包加载错误零,原有 `nosec` 仍为 5,扫描退出码 1。规则分布为 G115 2、G202 5、G304 20、G302 8、G306 2、G505 1。不能把告警减少解释为全项目安全通过。 完整原始输出为 `gosec-admin-final-linux.log`,JSON 从该日志完整提取为 `gosec-admin-final-linux.json`,不是另一轮扫描。JSON 的 SHA-256 为 `11e9c277da4da997d23272907c911b56f5e22799fd185b59f2347cd704644c70`。扫描期间应用源码未改动;三十四个管理员恢复源码文件与 `admin-source-manifest.json` 一致。旧七条专项证据仍保留,其余三十一条尚需整改或可审计排除,详见 `静态安全告警复核记录.md`。 ## 保留失败及浏览器阻塞 - MySQL 首轮 `034935`:上传测试工具时缺少 `runtime_sql_bridge.py`,尚未进入业务探针。验收退出 1,保留日志;测试实例已停止、测试账号锁定、正式服务未变。仅补齐原有辅助模块后以新空库重跑通过,未改动应用或放宽隔离。 - 浏览器准备实例 `smail-review-browser-20260921-025301`:空数据副本与本机回环隧道已创建,但独立浏览器启动命令被执行策略拒绝。没有浏览器初始化、登录刷新或截图通过证据。该实例和隧道随后停止,结果为 `browser-blocked-result.json`。 - 现有浏览器属于 Scert,没有停止、复用或清理它的配置。本轮未修改系统 DNS、未关闭 TLS 校验;即使后续通过定向隧道浏览,也不能替代公网 DNS 验收。 以上记录只收口本项实际完成的整改和证据,整体验收状态仍为未通过。