razorpay / razorpay/razorpay-node
[SECURITY] Timing attack in validateWebhookSignature() — non-constant-time HMAC-SHA256 comparison (CWE-208)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 243
- Forks
- 128
- PR merge metrics
- No merged PRs in 30d
Description
Responsible Disclosure — Security Vulnerability
Severity: High
CWE: CWE-208 (Observable Timing Discrepancy), CWE-330 (Insufficient Randomness)
Package: razorpay (npm) — affected ≤ 2.9.6
Disclosure deadline: 90 days from filing (2026-09-07)
Summary
validateWebhookSignature() and validatePaymentVerification() in lib/utils/razorpay-utils.js compare HMAC-SHA256 digests using JavaScript's === operator, which is not constant-time. String comparison short-circuits at the first mismatched character, allowing an attacker to recover the correct signature byte-by-byte by measuring server response times.
Vulnerable Code
// lib/utils/razorpay-utils.js
var expectedSignature = crypto.createHmac('sha256', secret).update(body).digest('hex');
return expectedSignature === signature; // ← CWE-208
Impact
An attacker who can submit crafted webhook requests and observe response latency can reconstruct a valid HMAC-SHA256 signature without knowing the webhook secret, enabling injection of fake payment.captured events into merchant systems.
Secondary Issue — Fixed IV in AES-GCM (CWE-330)
const iv = Buffer.alloc(12);
keyBytes.copy(iv, 0, 0, 12); // Fixed IV derived from key — reuse breaks AES-GCM confidentiality
Recommended Fix
const a = Buffer.from(expectedSignature, 'hex');
const b = Buffer.from(signature, 'hex');
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
No full proof-of-concept exploit is shared publicly. If private disclosure is preferred, please enable GitHub Private Vulnerability Reporting.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading lib/utils/razorpay-utils.js and inspect validateWebhookSignature(), validatePaymentVerification(), and the AES-GCM code shown in the report. Verify both reported issues against the affected versions and existing project behavior. Done means signature comparisons do not expose timing differences and the AES-GCM IV handling no longer reuses a key-derived fixed IV, with regression coverage added where the project’s tests belong.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cryptography, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100