razorpay / razorpay/razorpay-node

[SECURITY] Timing attack in validateWebhookSignature() — non-constant-time HMAC-SHA256 comparison (CWE-208)

Open
#479 0 comments 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.