ssl/ 目录下有两个私钥被提交进了仓库,.gitignore 也没拦住。我把证书内容拉出来核了一遍,其中一张建议尽快处理:
| 文件 |
Subject |
Issuer |
有效期 |
私钥是否配对 |
ssl/LocalRootCA.crt |
CN=LocalRootCA |
自签根 CA |
2024-04-03 → 2034-04-01 |
未入库 |
ssl/naominet.live.crt |
CN=*.naominet.live |
CN=LocalRootCA |
2024-04-03 → 2034-02-10 |
是,配 ssl/naominet.live.key |
ssl/portal.naominet.live.crt |
CN=portal.naominet.live |
自签 |
2026-09-02 → 2027-09-02 |
是,npm run gen:cert 生成的那对 |
核对用的命令:
openssl x509 -in ssl/naominet.live.crt -noout -subject -issuer -dates
openssl verify -CAfile ssl/LocalRootCA.crt ssl/naominet.live.crt # -> OK
openssl x509 -in ssl/naominet.live.crt -noout -pubkey | openssl md5
openssl rsa -in ssl/naominet.live.key -pubout | openssl md5 # -> 指纹一致,19c3568d…
严重性我想说准一点,不夸。先补一个影响判断的细节:ssl/naominet.live.crt 是 X509 v1、零扩展、完全没有 SAN,主机名只写在 CN 里(对比 portal. 那张是 v3,SAN 里有 portal.naominet.live, localhost, 127.0.0.1)。这决定了它到底能骗过谁:
-
泄漏的是 *.naominet.live 的叶子私钥,aqua. / portal. / status. 都在 CN 通配符的覆盖范围内,有效到 2034 年;
-
根 CA 的私钥(LocalRootCA.key)没入库,所以伪造不出新证书;
-
无 SAN 意味着现代浏览器不认它 —— Chrome 58 / Firefox 48 之后,主机名一律只按 SAN 匹配、CN 被完全忽略。所以拿这张证书去中间人浏览器流量,会在主机名校验这一步就被挡下。这一条我只在 OpenSSL 系客户端上实测过,浏览器侧受本机信任库限制没能单独隔离验证(想用 --ignore-certificate-errors-spki-list 绕过信任问题,结果那个开关把主机名校验一起绕过了,连 evil.example.com 都放行,测不出结论),所以浏览器那半句是依据已知策略而非实测。
-
真正能骗到的是仍回退到 CN 做校验的客户端,也就是 OpenSSL 那一系。我用这张证书实际起了个 HTTPS 服务测:
| 客户端 |
请求的 servername |
结果 |
| Node / OpenSSL |
aqua.naominet.live |
通过(回退到 CN *.naominet.live) |
| Node / OpenSSL |
portal.naominet.live |
通过 |
| Node / OpenSSL |
deep.sub.naominet.live |
拒绝 |
| Node / OpenSSL |
evil.example.com |
拒绝 |
第三行的报错原文是 Hostname/IP does not match certificate's altnames: ... is not cert's CN: *.naominet.live,正好反证它确实在用 CN 匹配(通配符只覆盖一层)。
-
最后还有个前提:得有机器把 LocalRootCA 装进信任库。这一点我没找到证据 —— 仓库里没有任何地方引导信任这个根 CA(README 让你装的是 portal. 那张自签证书),这三个文件本身也全无引用。当初它们怎么用起来的、有没有机器信过这个根,我从仓库里看不出来。
所以我倒不觉得"反正是自签的,不用管"。但也不想把它说成"拿到 key 就能完整中间人全部 *.naominet.live 流量"——准确的说法是:对浏览器无效,对仍按 CN 校验的客户端有效,且需要目标机器信任这个根 CA。
另外这三个文件在仓库里已经没有引用了,看着就是遗留物:
scripts/gen-cert.mjs:11-12 只生成 portal.naominet.live.crt/key,复现不出上面三个;
vite.config.ts:12-13 和 scripts/serve-legacy-baseline.mjs:12-13 都只读 portal. 那一对;
- 全仓也 grep 不到
naominet.live.crt / naominet.live.key / LocalRootCA.crt。
改的话是这样:
git rm --cached ssl/LocalRootCA.crt ssl/naominet.live.crt ssl/naominet.live.key
.gitignore 加一条:
# 本地开发证书,用 npm run gen:cert 重新生成
/ssl/
然后顺手轮换一下:凡是把 LocalRootCA 装进过信任库的机器,都该把这个根 CA 摘掉,改用 gen-cert.mjs 那种单域名自签证书。
最后提一句历史问题——这几个文件在 git 历史里还在,要彻底清掉得用 git filter-repo 重写。社区项目值不值得做到这一步,你们自己判断。但至少先让 key 从当前代码里消失。
ssl/目录下有两个私钥被提交进了仓库,.gitignore也没拦住。我把证书内容拉出来核了一遍,其中一张建议尽快处理:ssl/LocalRootCA.crtCN=LocalRootCAssl/naominet.live.crtCN=*.naominet.liveCN=LocalRootCAssl/naominet.live.keyssl/portal.naominet.live.crtCN=portal.naominet.livenpm run gen:cert生成的那对核对用的命令:
严重性我想说准一点,不夸。先补一个影响判断的细节:
ssl/naominet.live.crt是 X509 v1、零扩展、完全没有 SAN,主机名只写在 CN 里(对比portal.那张是 v3,SAN 里有portal.naominet.live, localhost, 127.0.0.1)。这决定了它到底能骗过谁:泄漏的是
*.naominet.live的叶子私钥,aqua./portal./status.都在 CN 通配符的覆盖范围内,有效到 2034 年;根 CA 的私钥(
LocalRootCA.key)没入库,所以伪造不出新证书;无 SAN 意味着现代浏览器不认它 —— Chrome 58 / Firefox 48 之后,主机名一律只按 SAN 匹配、CN 被完全忽略。所以拿这张证书去中间人浏览器流量,会在主机名校验这一步就被挡下。这一条我只在 OpenSSL 系客户端上实测过,浏览器侧受本机信任库限制没能单独隔离验证(想用
--ignore-certificate-errors-spki-list绕过信任问题,结果那个开关把主机名校验一起绕过了,连evil.example.com都放行,测不出结论),所以浏览器那半句是依据已知策略而非实测。真正能骗到的是仍回退到 CN 做校验的客户端,也就是 OpenSSL 那一系。我用这张证书实际起了个 HTTPS 服务测:
aqua.naominet.live*.naominet.live)portal.naominet.livedeep.sub.naominet.liveevil.example.com第三行的报错原文是
Hostname/IP does not match certificate's altnames: ... is not cert's CN: *.naominet.live,正好反证它确实在用 CN 匹配(通配符只覆盖一层)。最后还有个前提:得有机器把
LocalRootCA装进信任库。这一点我没找到证据 —— 仓库里没有任何地方引导信任这个根 CA(README 让你装的是portal.那张自签证书),这三个文件本身也全无引用。当初它们怎么用起来的、有没有机器信过这个根,我从仓库里看不出来。所以我倒不觉得"反正是自签的,不用管"。但也不想把它说成"拿到 key 就能完整中间人全部
*.naominet.live流量"——准确的说法是:对浏览器无效,对仍按 CN 校验的客户端有效,且需要目标机器信任这个根 CA。另外这三个文件在仓库里已经没有引用了,看着就是遗留物:
scripts/gen-cert.mjs:11-12只生成portal.naominet.live.crt/key,复现不出上面三个;vite.config.ts:12-13和scripts/serve-legacy-baseline.mjs:12-13都只读portal.那一对;naominet.live.crt/naominet.live.key/LocalRootCA.crt。改的话是这样:
.gitignore加一条:然后顺手轮换一下:凡是把
LocalRootCA装进过信任库的机器,都该把这个根 CA 摘掉,改用gen-cert.mjs那种单域名自签证书。最后提一句历史问题——这几个文件在 git 历史里还在,要彻底清掉得用
git filter-repo重写。社区项目值不值得做到这一步,你们自己判断。但至少先让 key 从当前代码里消失。