一、事情经过
7 月 30 日,我部署在 api.***.cloud 上的 CPA(CLIProxyAPI v7.2.100)出现异常调用 查日志后发现:有人通过管理接口给自己签发了一把 API key,然后用我的上游额度白嫖模型调用
攻击时间线非常清晰:
12:29:57 – 12:31:02:来自全球几十个不同 IP 的请求密集访问 GET /v0/management/api-keys,全部返回 401,每次耗时 58–61ms;此后爆破全天不断(14:48、深夜均有波次,一直持续到次日凌晨 01:44,全天共 249 次尝试)
13:18:54:一个新 IP(103.151.173.208 日本IKUUU VPN 出口)第一次请求就返回 200
13:18:55:同一 IP 发起 PUT /v0/management/api-keys,日志打出 api-keys count: 1 -> 2——攻击者的 key 被写进了我的配置文件
13:47:另一个 IP(155.117.84.62 香港 Lshiy 机房)打开管理面板 /management.html,并拉走了 /v0/management/config、/auth-files、/openai-compatibility 等接口——配置里所有上游凭据全部泄露
13:58 起:155.117.84.61–64 开始用注入的 key 调用 /v1/responses、/v1/messages(零星几次,试探性质)
17:26 – 次日 08:21:第三个 IP(107.173.42.94 美国 RackNerd 代理型 IP)接管盗用,彻夜不间断调用 /v1/messages、/v1/responses、Gemini 接口共 3748 次——这段时间我几乎没用服务,全是攻击者在烧我的额度





Cloudflare 后台也印证了这波爆破:/v0/management/api-keys 以 252 次登顶 4xx 路径榜,攻击时段源站错误明显飙升
二、第一反应:是不是鉴权被绕过了?
管理接口的鉴权代码(internal/api/handlers/management/handler.go:265-397)看起来挺结实:
那问题来了:5 次失败就封 IP,他是怎么试出来的?
三、真正的漏洞:封禁机制基于一个可以随便伪造的 IP
问题出在 IP 的取法上 代码用 gin 框架的 c.ClientIP() 取客户端 IP(handler.go:272),而 gin 默认信任所有代理头——项目的 internal/api/server.go:296 用 gin.New() 创建引擎后,从未调用 SetTrustedProxies 收紧这个行为

这意味着:任何人只要在请求里加一个 X-Forwarded-For: 随便什么IP,服务器就把这个假 IP 当成客户端 IP。封禁计数器是按这个假 IP 算的,每换一个假 IP 就是一个"新用户",封禁永远不触发
日志里那波"来自全球几十个国家、一次一个、间隔 1–3 秒"的请求,根本不是什么僵尸网络——一台机器轮换请求头就能演出来
更糟的是,连 allow-remote: false(仅允许本机管理)这个开关也看同一个 IP(handler.go:337):伪造 X-Forwarded-For: 127.0.0.1 就直接被当成 localhost,形同虚设

四、复现验证
我用 v7.2.100 官方 release 二进制做了完整复现(本地弱密钥环境 + 对自己线上实例的只读验证),结果与受害日志逐条吻合:
同一 IP 连错 5 次 → 第 6 次 403 封禁(封禁机制本身有效)
每次换伪造的 X-Forwarded-For → 连续十几次全部 401,永不封禁,字典命中后返回 200
用爆出的密钥 PUT /v0/management/api-keys 写入新 key → 服务端日志打出 api-keys count: 1 -> 2,新 key 立即可用
固定伪造同一个假 IP 连试 → 401×5 后 403,证明封禁计数器确实是按(可伪造的)IP 算的
本文仅用于入侵复盘与防御提醒,复现均在本人环境及本人服务器上进行,请勿将所述方法用于未授权目标
五、上游修了吗?没有
我对比了 v7.2.100 到最新 v7.2.111 的全部 80 个提交:管理鉴权逻辑没有任何变化 GitHub 上有两个相关的 open PR(#3150 添加 trusted proxies 配置、#3160 加固代理认证),都没合并,也没有任何安全公告
也就是说:当前所有版本都存在这个问题,唯一有效的防线就是管理密钥本身的强度——只要你的管理面暴露公网、密钥又在常见字典里,被爆破只是时间问题
六、教训与加固清单
这次被黑的根因是三个问题叠加:管理面直接暴露公网、密钥强度不够,在攻击者字典里、防爆破机制被伪造请求头一招废掉
如果你也在用 CPA,也可以检查一下:
轮换一切:管理密钥换成 ≥24 位随机串;删掉不认识的 api-key;config 里所有上游 key、OAuth 凭据全部视为已泄露,全部轮换
关掉远程管理:不需要就置空 secret-key,整个 Management API 直接禁用
反代层收口:Nginx 务必覆盖客户端传来的 X-Forwarded-For(proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;),并对 /v0/management/ 加 IP 白名单或直接 deny
服务别直接暴露公网,只经反代暴露必要端口
看日志:搜 /v0/management/ 的访问记录,出现陌生 IP 的 200 就是出事信号
最后在想要不要给攻击者服务器上点压力呢 ^-^(bushi
