编号:L-07 严重级:low 工作线:监听与 HTTP(internal/listener、internal/httpx、serve 的 HTTP 装配) 来源:审查 P-20、P-21、P-26、A-17、P-24 依赖:无 被依赖:无
httpx.ClientIP
strings.Join(r.Header.Values("X-Forwarded-For"), ",")
tls.Config
subtle.ConstantTimeCompare
LockAdminIP
listener.ParseTrustedProxies
listener.New
internal/httpx/clientip.go、metrics.go;internal/listener/proxy.go、tls.go、server.go;internal/broker/ws.go(改为调用 httpx.ClientIP);cmd/nixmsg/serve.go(listener 装配段)。
internal/httpx/clientip.go
metrics.go
internal/listener/proxy.go
tls.go
server.go
internal/broker/ws.go
cmd/nixmsg/serve.go
ws.go 与 L-01 同文件,监听线内顺序合入;serve.go 只改 listener 装配段。
DidResume
以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。
listener.ProxySet.ClientIP
Header.Get
option forwardfor
listener/proxy.go:65-80
httpx/clientip.go:23-38
listener/server.go:298
tls.go:62-75
110-117
NewCertReloader
internal/listener/tls.go
!=
httpx/metrics.go:17-21
auth[len(prefix):] != token
internal/httpx/metrics.go
/metrics
internal/httpx/metrics.go:17-19
serve.go:262-291
tls.go:26-41
复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。
4059a15
已合入 origin/main 0c9b459。落地提交 7c926a0 fix: 修复 TLS ConnectionState、SPA 回退、健康检查与监听小问题 (#26)。
0c9b459
7c926a0
No dependencies set.
The note is not visible to the blocked user.
编号:L-07 严重级:low 工作线:监听与 HTTP(internal/listener、internal/httpx、serve 的 HTTP 装配) 来源:审查 P-20、P-21、P-26、A-17、P-24
依赖:无 被依赖:无
结论与统一方案
httpx.ClientIP一份,WS 也调用它;用strings.Join(r.Header.Values("X-Forwarded-For"), ",")合并多行;从右往左遇到无法解析的项就停下,回退到对端地址。(审查 P-20,身份区待核实第 2 条)tls.Config只建一次并复用;证书检查间隔改为 1–5 分钟。(审查 P-21)subtle.ConstantTimeCompare比较;可选失败计入LockAdminIP。(审查 A-17、P-26)listener.ParseTrustedProxies得到 ProxySet,只调用一次listener.New。(审查 P-24)改动文件
internal/httpx/clientip.go、metrics.go;internal/listener/proxy.go、tls.go、server.go;internal/broker/ws.go(改为调用httpx.ClientIP);cmd/nixmsg/serve.go(listener 装配段)。与其他问题的交互 / 冲突说明
ws.go 与 L-01 同文件,监听线内顺序合入;serve.go 只改 listener 装配段。
验收与测试
DidResume为真;先换证书后换私钥,在短周期内生效。问题明细(各区审查原文,证据含文件与行号)
[P-20] X-Forwarded-For 只读第一行,且遇到无法解析的项会继续向左取
listener.ProxySet.ClientIP和后台、注册用的httpx.ClientIP两份实现都用Header.Get,只取第一行 X-Forwarded-For。option forwardfor这类"另起一行"而不是合并到同一行的代理,第一行正是客户端自己伪造的值;listener/proxy.go:65-80;httpx/clientip.go:23-38。strings.Join(r.Header.Values("X-Forwarded-For"), ",")合并所有行;从右往左遇到无法解析的项就停下,回退到对端地址;两份实现合并成 httpx 一份,WS 也调用它。internal/httpx/clientip.go、internal/listener/proxy.go、internal/broker/ws.go[P-21] TLS:每个连接新建一个 tls.Config;证书检查间隔 1 小时
tls.Config,每个 Config 有自己的会话票据密钥,客户端重连永远要做完整握手。listener/server.go:298;tls.go:62-75、110-117。NewCertReloader里只建一次tls.Config并复用;检查间隔改为 1 到 5 分钟(只是 stat 文件,开销可以忽略)。internal/listener/tls.go、server.goDidResume为真;先换证书后换私钥,在短周期内生效。[P-26] /metrics 令牌用普通字符串比较
!=,比较耗时随匹配前缀变化,理论上存在时序侧信道。httpx/metrics.go:17-21(auth[len(prefix):] != token)。subtle.ConstantTimeCompare。internal/httpx/metrics.go[A-17]
/metrics令牌比较不是常量时间,也不限制尝试次数/metrics令牌用!=比较,可以不受限制地反复尝试。指标本身不含敏感明细,所以影响有限。internal/httpx/metrics.go:17-19。subtle.ConstantTimeCompare比较;可选:失败计入LockAdminIP。internal/httpx/metrics.go。[P-24] serve 调了两次 listener.New,泄漏一个证书重载 goroutine
listener.New已经启动了证书重载的 goroutine,这个实例随后被丢弃且从没关闭。之后每小时会重载两次、打两份日志;没配证书时"plaintext only"警告也打两遍。serve.go:262-291;tls.go:26-41。listener.ParseTrustedProxies得到 ProxySet,组装好路由后只调一次listener.New。cmd/nixmsg/serve.go复审基线:main
4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。已合入 origin/main
0c9b459。落地提交7c926a0fix: 修复 TLS ConnectionState、SPA 回退、健康检查与监听小问题 (#26)。