सामग्री पर जाएं

वेबहुक सिग्नेचर सत्यापन: क्यों HMAC-SHA256 भुगतान कॉलबैक पर भरोसा करने का एकमात्र सुरक्षित तरीका है

Payora7 मिनट पढ़ने का समयEN · RU · UK · ES · DE · FR · IT · PL · PT · HI · ZH

एक भुगतान वेबहुक आपके सर्वर पर आता है जो दावा करता है कि एक ऑर्डर का भुगतान किया गया है। क्या आप इस पर भरोसा करते हैं? यदि आप केवल HTTP कॉल के आधार पर ऑर्डर को क्रेडिट करते हैं — बिना किसी क्रिप्टोग्राफिक सिग्नेचर को सत्यापित किए — तो आपने हमलावरों को किसी भी ऑर्डर को भुगतान के रूप में चिह्नित करने का एक मुफ्त तरीका दे दिया है।

सत्यापन के बिना क्या गलत हो सकता है

एक असुरक्षित वेबहुक एंडपॉइंट को आसानी से शोषित किया जा सकता है। एक हमलावर को केवल आपका कॉलबैक URL जानने की आवश्यकता होती है - जो अक्सर ब्राउज़र के नेटवर्क टैब, एक सार्वजनिक GitHub रिपॉजिटरी, या एक परीक्षण चेकआउट से खोजा जा सकता है - और वे एक तैयार किया गया पेलोड POST कर सकते हैं:

POST /webhook HTTP/1.1
Content-Type: application/json

{"order_id": "1337", "status": "paid", "amount": "99.99"}

यदि आपका हैंडलर पढ़ता है $_POST['status'] === 'paid' और ऑर्डर को क्रेडिट करता है, तो हमलावर ने मुफ्त में $99.99 के सामान खरीद लिए। यह एक सैद्धांतिक हमला नहीं है; यह नियमित रूप से असुरक्षित भुगतान एकीकरणों के खिलाफ होता है।

HMAC-SHA256 साइनिंग कैसे काम करता है

HMAC (हैश-आधारित संदेश प्रमाणीकरण कोड) SHA-256 के साथ इस समस्या को हल करता है क्योंकि यह पेलोड को बिना गुप्त कुंजी के नकल करने योग्य नहीं बनाता। प्रक्रिया है:

  1. भुगतान गेटवे को आपका api_secret पता है - एक गुप्त जो केवल आपके और गेटवे के बीच साझा किया जाता है।
  2. वेबहुक भेजने से पहले, यह गणना करता है: HMAC-SHA256(timestamp + "." + body, api_secret)
  3. यह HTTP हेडर में इस हस्ताक्षर और टाइमस्टैम्प को शामिल करता है।
  4. आपका सर्वर वही HMAC फिर से गणना करता है और तुलना करता है। यदि वे मेल खाते हैं, तो पेलोड प्रामाणिक है।

एक हमलावर जो आपके api_secret को नहीं जानता, एक मान्य हस्ताक्षर नहीं बना सकता — SHA-256 एक एकतरफा फ़ंक्शन है। टाइमस्टैम्प पुनःप्रयोजन हमलों को रोकता है: यहां तक कि एक मान्य इंटरसेप्ट किया गया वेबहुक 5 मिनट बाद बेकार हो जाता है।

सत्यापन लागू करना: PHP

function verifyPayoraWebhook(string $secret): array {
    $ts   = $_SERVER['HTTP_X_PAYORA_TIMESTAMP'] ?? '';
    $sig  = $_SERVER['HTTP_X_PAYORA_SIGNATURE'] ?? '';
    $body = file_get_contents('php://input');

    // 1. reject stale timestamps (replay protection)
    if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) {
        http_response_code(400); exit('stale timestamp');
    }

    // 2. recompute and compare — hash_equals() prevents timing attacks
    $expected = hash_hmac('sha256', $ts . '.' . $body, $secret);
    if (!hash_equals($expected, $sig)) {
        http_response_code(401); exit('invalid signature');
    }

    return json_decode($body, true);
}

$event = verifyPayoraWebhook('sk_your_secret');
if ($event['status'] === 'paid') {
    creditOrder($event['order_id']);
}

सत्यापन लागू करना: Node.js

import crypto from 'node:crypto';

function verifyPayoraWebhook(req, rawBody, secret) {
    const ts  = req.headers['x-payora-timestamp'] ?? '';
    const sig = req.headers['x-payora-signature'] ?? '';

    if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
        return null; // stale
    }

    const expected = crypto
        .createHmac('sha256', secret)
        .update(`${ts}.${rawBody}`)
        .digest('hex');

    // timingSafeEqual prevents timing side-channel attacks
    return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))
        ? JSON.parse(rawBody)
        : null;
}

सामान्य गलतियाँ जो सत्यापन को तोड़ती हैं

1. कच्चे बाइट्स के बजाय एक पार्स किए गए शरीर को पढ़ना। एक्सप्रेस का json()मिडलवेयर JSON को फिर से अनुक्रमित करता है, जो कुंजी को पुनर्व्यवस्थित कर सकता है याWhitespace को बदल सकता है। हमेशा कच्चे अनुरोध शरीर को बफर करेंपहलेपार्सिंग, और HMAC के लिए इसका उपयोग करें।

2. एक समय-सुरक्षित तुलना के बजाय===।एक साधारण स्ट्रिंग तुलना समय की जानकारी लीक करती है जिसे सिग्नेचर बाइट को बाइट द्वारा अनुमान लगाने के लिए उपयोग किया जा सकता है। उपयोग करेंhash_equals()PHP में याcrypto.timingSafeEqual()Node में।

3. टाइमस्टैम्प जांच को छोड़ना।एक मान्य लेकिन पुराना वेबहुक जिसे एक नेटवर्क पर्यवेक्षक द्वारा कैप्चर किया गया था, उसे फिर से चलाया जा सकता है। 5-मिनट की विंडो (±300 सेकंड) इस जोखिम को समाप्त करती है जबकि उचित घड़ी के झुकाव को संभालती है।

4. सत्यापन से पहले कच्चे शरीर को लॉग करना।यदि आपकी लॉगिंग पाइपलाइन से समझौता किया गया है, तो लॉग की गई पेलोड्स को फिर से चलाया जा सकता है। पहले सत्यापित करें, बाद में लॉग करें।

हस्ताक्षर के परे: आइडेम्पोटेंसी

सत्यापन यह साबित करता है कि वेबहुक प्रामाणिक है; आइडेम्पोटेंसी यह सुनिश्चित करती है कि आप एक आदेश को दो बार क्रेडिट न करें। नेटवर्क गैर-200 प्रतिक्रियाओं पर वेबहुक को पुनः प्रयास करते हैं, इसलिए आपका हैंडलर एक ही घटना को कई बार प्राप्त कर सकता है। समाधान यह है कि एक webhook_id आपके डेटाबेस में और यदि आपने इसे पहले देखा है तो प्रोसेसिंग छोड़ें:

// pseudo-code
$id = $event['webhook_id'];
if (DB::exists('processed_webhooks', $id)) return respond(200);
DB::insert('processed_webhooks', $id);
creditOrder($event['order_id']);

साथ में, हस्ताक्षर सत्यापन और आइडेम्पोटेंसी आपको एक भुगतान एकीकरण प्रदान करते हैं जो सुरक्षित और लचीला दोनों है।पूर्ण एकीकरण गाइड देखेंPayora के पूर्ण वेबहुक संदर्भ के लिए।

Comments

शुरू करने के लिए तैयार हैं?

खाता बनाएँ और एक घंटे से भी कम समय में अपना पहला इनवॉइस जारी करें।

इस पृष्ठ का उत्तर क्या है