Executive brief
Pay is a payment processing engine for Ruby on Rails applications that handles billing integrations with providers like Paddle. Prior to version 11.6.2, a flaw in webhook signature verification allowed attackers to forge webhook messages through timing analysis, potentially enabling unauthorized billing changes, feature provisioning, refunds, or fake customer notifications without proper authorization.
Technical details
The vulnerability is a timing-based signature forgery attack (CWE-208, Timing Attack) in the PaddleBillingController#valid_signature? method. The code used Ruby's standard String#== operator to compare a computed 64-character SHA-256 HMAC digest with an attacker-controlled token from the Paddle-Signature header. Because String#== short-circuits on the first byte mismatch, an attacker can measure response timing across repeated requests to infer matching digest prefixes and eventually reconstruct a valid signature. The attack is unauthenticated and network-accessible at the /pay/webhooks/paddle_billing endpoint. Once a valid signature is forged, malicious webhooks are processed through Pay::Webhooks::ProcessJob with full authority to modify billing state. The fix, released in 11.6.2, replaces String#== with ActiveSupport::SecurityUtils.secure_compare, a constant-time comparison function, and adds a bytesize check for Rails compatibility.
Affected products
- Pay Pay prior to 11.6.2
Timeline
- 2026-09-14: disclosed
- 2026-07-06: patched: Version 11.6.2 released with fix using ActiveSupport::SecurityUtils.secure_compare