fix: 修复独立审计发现的两个高风险缺陷(H1 凭证写入竞态 / H2 管理接口默认开放) - #6
Merged
Merged
Conversation
来源:AUDIT-2026-09-15.md 的独立审计(不依赖 TECHNICAL-DOC 既有结论)。
## H1 跨进程并发写凭证,会永久丢失 refreshToken
原实现用【固定】临时文件名:
tmp := a.FilePath + ".tmp"
`Auth.mu` 只保护进程内,而实际是多进程架构:
serverd —— 请求前预刷新(handler.go:472)、定时保活(scheduler.go:374/414)
ctl —— 独立子进程,面板点「刷新令牌」时写同一文件(ctl/main.go:317)
两进程各有独立的内存副本与锁,进程间无互斥,且写同一个 tmp 文件,于是:
1) 内容交错;
2) 一方先 rename 走,另一方 rename 时 tmp 已不存在。
实测(并发 800 次写入):旧实现失败 292 次。
更严重的是**逻辑丢失**:上游 refresh 会轮换 refreshToken,
两进程各拿到新值,后落盘者覆盖先落盘者;若落盘的是已被轮换失效的那个,
账号下次刷新即失败,需人工重登(已确定性复现)。
文件本身不损坏(rename 原子),丢的是「较新的那个值」。
修复:新增 internal/atomicfile 包
- 临时文件改用 os.CreateTemp 生成唯一名(消除写交错与 rename 竞争)
- 写入前对目标路径加跨进程 flock(unix;非 unix 退化为无锁,仅保证可交叉编译)
- 写入后 fsync 文件与目录
auth.go 与 pool.go(state 文件同理)一并改用。
单测覆盖:并发写零错误、无残留临时文件、权限 0600、覆盖写不残留旧内容。
## H2 面板默认无登录,管理接口对整个内网开放
链路:
config.yaml 默认 webui_user/pass 为空
→ load_options() 中 "" 不是 None,会**覆盖**掉 DEFAULT_OPTIONS 的 admin/admin
→ _webui_enabled() == False
→ _mgmt_authorized() 回退到 _is_local()
→ _is_local() 对 172.30.* / 172.16.* / 192.168.* / 10.* 一律放行
→ 而 config.yaml 有 ports: 7870/tcp: 7870(映射到宿主机)
后果:局域网内任意设备访问 http://<HA>:7870/api/* 即可无认证
调用管理接口(账号列表、刷新令牌、修改配置)。
根因:ingress 转发与 LAN 直连的**源 IP 都是内网**
(172.30.x vs 192.168.x),仅凭 IP 段无法区分。
修复:拆分判断维度
- _is_loopback() 仅回环
- _is_private() 内网段(保留,注释说明其不可用于鉴权)
- _is_ingress() 按 /api/hassio_ingress/ 路径前缀识别(与既有第 1081 行判断一致)
- _mgmt_authorized() 未启用登录时只信任 ingress 与本机回环,不再信任整个内网
同时移除已无调用点的 _is_local(),避免误用。
验证:scripts/test_mgmt_auth.py 真值表 11 个用例全通过,
覆盖 ingress / 回环 / LAN / 公网 / 已登录 / 启用登录各组合。
## 其他
- .gitignore 增加 __pycache__/ 与 *.pyc,并移除已误入库的 .pyc
- 新增 AUDIT-2026-09-15.md(完整审计报告,含与 TECHNICAL-DOC 的交叉比对)
## 验证
go build ./... PASS
go vet ./... PASS
go test ./... PASS(含 atomicfile 4 个新单测)
scripts/test_mgmt_auth.py 11/11 通过
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description / 变更描述
修复独立审计(
AUDIT-2026-09-15.md)发现的两个高风险缺陷。审计不依赖
TECHNICAL-DOC.md的既有结论,每条均经代码定位或可复现实验验证。🔴 H1 跨进程并发写凭证,会永久丢失 refreshToken
位置:
src/internal/auth/auth.go:204(及pool.go的 state 文件)原实现用固定临时文件名:
Auth.mu只保护进程内,而实际是多进程架构:serverdhandler.go:472serverdscheduler.go:374/414ctl(独立进程)ctl/main.go:317两进程各有独立的内存副本与锁,进程间无互斥,且写同一个 tmp 文件。
实测(并发 800 次写入):旧实现失败 292 次。
更严重的是逻辑丢失 —— 上游 refresh 会轮换 refreshToken(旧的即失效):
(已确定性复现)
修复:新增
internal/atomicfile包os.CreateTemp生成唯一名 → 消除写交错与 rename 竞争flock(unix;非 unix 退化为无锁,仅保证可交叉编译)fsync文件与目录auth.go与pool.go(state 文件同理)一并改用。🔴 H2 面板默认无登录,管理接口对整个内网开放
链路:
后果:局域网内任意设备访问
http://<HA>:7870/api/*即可无认证调用管理接口(账号列表、刷新令牌、修改配置)。
根因:ingress 转发(
172.30.x)与 LAN 直连(192.168.x)的源 IP 都是内网,仅凭 IP 段无法区分。
修复:拆分判断维度
_is_loopback()仅回环_is_private()内网段(保留,注释说明其不可用于鉴权)_is_ingress()按/api/hassio_ingress/路径前缀识别(与既有login_ui.py:1081的同款判断一致)_mgmt_authorized()未启用登录时只信任 ingress 与本机回环同时移除已无调用点的
_is_local(),避免误用。其他
.gitignore增加__pycache__/与*.pyc,并移除已误入库的.pycAUDIT-2026-09-15.md(完整审计报告)验证
H1 对照实验(同一测试,旧实现 vs 新实现):
H2 真值表:11 个用例覆盖 ingress / 回环 / LAN / 公网 /
已登录 / 启用登录各组合,全通过。关键差异:
192.168.x/10.xType of change / 变更类型
Testing / 测试
H2 有真值表,Go 侧 build/vet/test 全绿
Checklist / 自检清单