安全研究言零的博客
newapi Stripe Webhook 充值漏洞深度分析 - 空密钥绕过支付验证
联系方式 & 交流群 QQ : 46333839 微信 : GOV-HACK 进微信群请联系博主,各位觉得文章对你有帮助的话可否打赏一些呀~ 时效性提醒(2026-07 更新):该漏洞已在 newapi v0.12.10 中修复。如果你正在使用 newapi,请确认已升级到修复版本。本文保留仅供支付安全设计参考。 漏洞概述 2026 年 4 月 16 日,LINUX DO 社区用户披露了 newapi 项目中的一个严重充值漏洞。攻击者可在未配置 Stripe 密钥的情况下,通过伪造签名绕过支付验证,实现零成本任意…
内容摘要
联系方式 & 交流群
QQ : 46333839
微信 : GOV-HACK
进微信群请联系博主,各位觉得文章对你有帮助的话可否打赏一些呀~
时效性提醒(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/webhook 2. 通过初始审计(或黑盒探测)确认服务端使用了空的 StripeWebhookSecret 3. 获取或猜测 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 hmac import 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 }
总结
这是一个典型的配置缺陷 + 逻辑漏洞组合:
配置缺陷:允许空密钥存在
逻辑漏洞:未验证密钥有效性即进行签名计算
安全配置项必须在上层做有效性校验,不能依赖底层库的"隐式行为”。空字符串和"未配置"是两回事:前者在 HMAC 计算中完全合法,后者才应该被拦截。
参考资料
new-api v0.12.10 Release Notes
Stripe Webhook Security Best Practices
本文基于社区披露信息进行分析,仅供安全研究参考。请合法合规使用,未经授权不得对任何系统进行测试。
资讯来源
言零的博客