# 审核专家 B:CHFS-NG 代码安全审计报告 - **审核人**:审核专家 B - **日期**:2026-10-02 - **对象**:`/root/chfs-ng-research`(Go,11 个非测试源文件,约 3900 行) - **生产环境**:`chfs-ng.service`(root 用户),`bin/chfs-ng -file chfs.ini`,监听 *:18999 - **验证**:`go vet ./...` 干净;`go test ./...` 全通过 - **结论**:**有条件通过** —— 代码层面的路径防御与权限检查扎实,但生产部署配置存在阻断项,另有 2 高/3 中需处理 --- ## 【阻断】生产配置使用弱密码 admin123,明文存储且文件全局可读 **【严重度:阻断】** **【位置】** `/root/chfs-ng-research/chfs.ini`(生产配置文件) **【问题描述】** 生产 `chfs.ini` 中 `[admin]` 的密码为明文 `admin123`,文件权限为 `-rw-r--r--`(全局可读)。 服务监听 `*:18999`(所有网卡),主机防火墙 INPUT 策略为 ACCEPT 且无规则,等同于直接暴露公网。 远程攻击者无需任何前置条件,直接用 `admin/admin123` 登录即获得全部文件读写删除权限。 **【利用/触发条件】** `curl -X POST http://:18999/chfs/session -d "user=admin&pwd=admin123"` 即得管理员 JWT。 **【修复建议】** 1. 立即更换高强度密码;2. `chmod 600 chfs.ini`;3. 配置 `allow` IP 白名单或置于反向代理后; 4. 考虑主机防火墙限制 18999 入站。 --- ## 【高】/chfs/exist 未鉴权的文件存在性预言机 **【严重度:高】** **【位置】** `internal/api/server.go:512` `func (s *Server) exist` **【问题描述】** 该端点全程未调用 `account(r, s)`,未做任何权限检查:`GET /chfs/exist?file=` 返回 201/404 直接泄露任意路径是否存在。所有其他文件端点都有 `MaskFor` 检查,唯独此处遗漏, 与 README 宣称的"完善的权限体系"不一致。 **【利用/触发条件】** 当前生产 guest=r,攻击者本就可列目录,实际影响有限;但在 guest 被收紧的部署中, 该端点绕过权限模型造成信息泄漏。 **【修复建议】** 加上 `if !account(r, s).MaskFor(p).HasRead() { 403 }`,与其他端点保持一致。 --- ## 【高】缩略图接口无文件大小限制,可被解压炸弹打 OOM **【严重度:高】** **【位置】** `internal/api/server.go:1164` `func (s *Server) thumbnail` **【问题描述】** `image.Decode(f)` 会把整个文件读入内存解码,而此前没有任何文件大小检查。 缓存层虽有 8MB/64 项上限(`thumbnail_cache.go`),但 decode 本身在缓存之外无限制。 攻击者(生产环境 guest 即有 read 权限)可请求超大文件或特制解压炸弹 (小文件声明超大宽高,如 PNG 头声明 100000x100000 → 数十 GB 内存分配),直接 OOM 杀死服务。 **【利用/触发条件】** `GET /chfs/thumbnail?file=<大文件或炸弹图片>`,无需登录(guest 可读)。 **【修复建议】** `Stat` 后检查 `fi.Size()`,超过阈值(如 32MB)直接拒绝;或对 decode 加内存上限。 --- ## 【中】目录 ZIP 下载无总大小/数量限制,CPU 与带宽耗尽 **【严重度:中】** **【位置】** `internal/api/server.go:1290` `downloadSelectedZip`、`1368` `downloadDir` **【问题描述】** 两个接口把目录树流式打包为 zip 直接写回 response,全程无总字节数、无文件数上限 (`downloadSelectedZip` 仅限制了顶层 path 参数 ≤100 个,但每个可为超大目录;`downloadDir` 无任何限制)。 `zip.Deflate` 是 CPU 密集型,多并发请求可打满 CPU 并占满带宽。guest 可读即触发。 **【修复建议】** 增加可配置的打包上限(总大小、文件数),超限返回 413;或对该接口加并发信号量。 --- ## 【中】上传无单文件/总量配额,可填满磁盘 **【严重度:中】** **【位置】** `internal/api/server.go:838` `upload`、`937` `upload2`、`savePart` **【问题描述】** `ParseMultipartForm` 仅限制内存部分(64MB/128MB),`savePart` 的 `io.Copy(f, in)` 无任何大小上限。 有写权限的用户可上传超大文件填满磁盘。当前生产仅 admin 有写权限,实际影响有限, 但项目主打"动态权限控制"(会给普通用户开写权限),属于设计缺口。 **【修复建议】** 增加 `upload.max-size` 配置项,`savePart` 中用 `io.LimitReader` 截断并报错。 --- ## 【中】默认删除进系统回收站且永不清理,磁盘被"已删除"文件占满 **【严重度:中】** **【位置】** `internal/storage/storage.go` `Trash`;`internal/config/config.go` 默认 `FileRemove: 1` **【问题描述】** 默认 `file.remove=1` 走 `wastebasket.Trash`(`~/.local/share/Trash`),该库无自动清理机制 (只有模式 2 的 `MoveToTrash` 有 30 天清理)。长期运行的服务删除的文件永久占用磁盘, `df` 与用户认知不一致,最终表现为神秘的磁盘占满。 **【修复建议】** 对模式 1 也加定期清理(如 30 天),或在文档中明确说明并给出清理方式。 --- ## 【中低】search 无结果数量上限 **【严重度:中低】** **【位置】** `internal/api/server.go:1009` `func (s *Server) search` **【问题描述】** 全树递归遍历,结果集无上限一次性 JSON 返回。超大目录树下内存与响应体膨胀, guest 可直接触发。 **【修复建议】** 加 `limit` 参数(默认如 500),超限截断并提示。 --- ## 【低】ipFilter 不识别 X-Forwarded-For,反代后 allow 白名单失效 **【严重度:低】** **【位置】** `internal/api/server.go:2065` `ipFilter` / `clientIP` **【问题描述】** `clientIP` 只读 `r.RemoteAddr`。README 建议生产用 HTTPS 反向代理, 一旦接入反代,`allow` 看到的永远是代理 IP,白名单名存实亡(要么全放行要么全拒绝)。 **【修复建议】** 支持可信代理 + `X-Forwarded-For` 解析,或在文档中明确说明该限制。 --- ## 【低】密码哈希为无盐 SHA256 **【严重度:低】** **【位置】** `internal/auth/auth.go` `func PasswordHash` **【问题描述】** `users.json` 中存储 `hex(sha256(password))`,无盐。文件泄漏后彩虹表可直接破解弱密码。 **【修复建议】** 改用 bcrypt(`golang.org/x/crypto/bcrypt`),并做旧哈希的兼容迁移。 --- ## 已验证为安全的关键点(正面结论) 1. **JWT 密钥管理正确**:`main.go` 在未配置 `jwt.secret` 时生成随机 32 字节密钥并以 0600 权限持久化 到 `users.json.jwt-secret`(生产已生效,文件存在)。`auth.go` 中的硬编码常量在 server 路径中不可达。 2. **路径穿越防御扎实**:`unsafePath`(NUL + `..` 段)与 `storage.real()` 双层防御;`real()` 对最近存在祖先 做 `EvalSymlinks`,连路径中间的符号链接逃逸都能拦住。多根目录经 `Multi` 路由后仍走同一检查。 3. **权限检查完整**:除 `exist` 外所有 mutating 端点都有 `MaskFor` 检查;`rename` 为 create-only; `remove`/`transfer` 有递归 preflight;WebDAV 有独立 `webdavAuthFS` 包装,Move/Copy/RemoveAll 均有 `checkTree`。 4. **上传 preflight**:`upload`/`upload2` 全量校验后再落盘,失败回滚已保存分片;`createExclusive` 防并发覆盖。 5. **Cookie 安全**:JWT cookie 为 `HttpOnly`;生产 guest 为只读。 6. **生产密钥文件权限正确**:`users.json.jwt-secret` 为 0600。 --- ## 总体结论 **有条件通过**。 代码本身质量不错:路径穿越防御、权限模型、并发处理都经过认真设计,测试全过、vet 干净。 真正的风险在**生产部署**:`admin123` 弱密码 + 公网直连 + 无防火墙是阻断项,必须立即处理。 代码层面需修:`exist` 鉴权缺失(高)、缩略图 OOM(高),以及 ZIP/上传/回收站/搜索的资源限制(中)。 修完阻断项和高优先级后可认为达到生产安全标准。