newapi Stripe Webhook 充值漏洞深度分析 - 空密钥绕过支付验证

联系方式 & 交流群
- 微信: gzs-47
进微信群请联系博主,各位觉得文章对你有帮助的话可否打赏一些呀~
⚠️ 时效性提醒(2026-07 更新):该漏洞已在 newapi v0.12.10 中修复。如果你正在使用 newapi,请确认已升级到修复版本。本文保留仅供支付安全设计参考。
漏洞概述
2026 年 4 月 16 日,LINUX DO 社区用户披露了 newapi 项目中的一个严重充值漏洞。攻击者可在未配置 Stripe 密钥的情况下,通过伪造签名绕过支付验证,实现零成本任意金额充值。
影响版本:v0.12.10 之前
修复版本:v0.12.10
漏洞类型:签名验证绕过 / 支付逻辑缺陷
风险等级:🔴 严重(直接资金损失)
攻击流程详解
第一阶段:信息收集
1. 发现目标暴露了 Stripe Webhook 端点 /api/stripe/webhook2. 通过初始审计(或黑盒探测)确认服务端使用了空的 StripeWebhookSecret3. 获取或猜测 client_reference_id 的格式(如 USR + 数字 + 随机字符串 + 时间戳)攻击者首先识别出目标的 Webhook 端点,并探测到服务端未正确配置 webhook_secret(空字符串)。这是漏洞利用的关键前提。
第二阶段:伪造签名
Stripe 的签名机制:
signed_payload = "{timestamp}.{json_body}"v1 = HMAC-SHA256(webhook_secret, signed_payload)Header = "t={timestamp},v1={v1}"当 webhook_secret = ““(空字符串)时:
- HMAC-SHA256 不会报错,正常计算
- 任何人都能对任意 payload 算出与服务端一致的签名
- 服务端调用
webhook.ConstructEventWithOptions()验签 → 通过 ✅
这是整个漏洞的核心:空密钥仍能通过 HMAC 计算,导致签名验证形同虚设。
第三阶段:构造恶意事件
伪造一个 checkout.session.completed 事件:
{ "type": "checkout.session.completed", "data": { "object": { "client_reference_id": "目标用户/订单 ID", "status": "complete", "payment_status": "paid", "amount_total": 任意金额 } }}攻击者可指定任意 client_reference_id 和 amount_total,实现定向充值和任意金额。
第四阶段:发送请求
向 Webhook 端点发送带有伪造签名的 POST 请求,服务端处理流程:
收到请求 ↓用空密钥验签 → 通过 ✅ ↓解析事件类型 → checkout.session.completed ↓读取 client_reference_id 查找订单 ↓标记订单已支付,给用户充值第五阶段:结果
| 项目 | 说明 |
|---|---|
| 用户余额 | 被充入攻击者指定的金额 |
| Stripe 实际收款 | $0(从未发生真实支付) |
| Stripe Dashboard | 无对应交易记录 |
| 服务端日志 | 看起来像正常的 Webhook 回调 |
技术原理分析
为什么空密钥能通过验签?
HMAC-SHA256 算法本身不验证密钥长度。当密钥为空字符串时:
import hmacimport hashlib
# 空密钥仍能计算signature = hmac.new( b"", # 空密钥 b"timestamp.body", hashlib.sha256).hexdigest()# 结果:有效的 64 字符十六进制字符串服务端代码若未检查 webhook_secret 是否为空,就会用空密钥验签,导致攻击者可以:
- 获取相同的 payload
- 用空密钥计算相同的签名
- 伪造出”合法”的请求
正确的防御姿势
# ❌ 错误:未检查密钥是否为空if not webhook_secret: webhook_secret = "" # 危险!
# ✅ 正确:强制要求配置密钥if not webhook_secret or len(webhook_secret) < 32: raise ConfigurationError("Stripe webhook secret must be configured")影响范围
直接受影响
- 所有使用 newapi v0.12.10 之前版本
- 未配置或错误配置
StripeWebhookSecret的实例 - 依赖 Stripe Webhook 进行充值确认的业务
潜在风险
- 资金损失:攻击者可零成本获取服务额度
- 数据污染:虚假交易记录污染财务数据
- 合规风险:支付审计无法通过
修复建议
立即措施
- 升级版本:立即升级到 v0.12.10 或更高版本
- 检查配置:确认
StripeWebhookSecret已正确配置(非空) - 审计日志:检查历史 Webhook 记录,排查异常充值
长期加固
- 启动检查:服务启动时强制验证关键配置项
- 签名验证增强:除 HMAC 外,增加时间戳窗口校验
- 对账机制:定期与 Stripe Dashboard 对账,发现不一致立即告警
- 监控告警:对大额充值、频繁充值设置阈值告警
代码修复示例
v0.12.10 的修复逻辑(推测):
// 修复前func (s *Service) VerifyWebhook(payload []byte, signature string) (*Event, error) { secret := s.config.StripeWebhookSecret // 可能为空 event, err := webhook.ConstructEvent(payload, signature, secret) return &event, err}
// 修复后func (s *Service) VerifyWebhook(payload []byte, signature string) (*Event, error) { if s.config.StripeWebhookSecret == "" { return nil, errors.New("stripe webhook secret not configured") } event, err := webhook.ConstructEvent(payload, signature, s.config.StripeWebhookSecret) return &event, err}总结
这是一个典型的配置缺陷 + 逻辑漏洞组合:
- 配置缺陷:允许空密钥存在
- 逻辑漏洞:未验证密钥有效性即进行签名计算
教训:安全配置项必须在上层进行有效性校验,不能依赖底层库的”隐式行为”。空字符串不是”未配置”,而是”配置了空值”——这两者有本质区别。
参考资料
本文基于社区披露信息进行分析,仅供安全研究参考。请合法合规使用,未经授权不得对任何系统进行测试。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














