Webhook हस्ताक्षर, समझाया गया

Webhook हस्ताक्षर एक छोटी स्ट्रिंग होती है जो भेजने वाली सेवा हर डिलीवरी के साथ जोड़ती है — एक HMAC, जो अनुरोध के ठीक-ठीक बाइट्स पर उस रहस्य के साथ गणना किया जाता है जिसे केवल भेजने वाला और प्राप्त करने वाला साझा करते हैं। इसका अस्तित्व इसलिए है ताकि प्राप्त करने वाला सर्वर कोई कार्रवाई करने से पहले दो सवालों के जवाब दे सके: क्या यह सचमुच उसी सेवा से आया है जिसका यह दावा करता है, और क्या बॉडी ठीक उसी रूप में पहुँची जिस रूप में भेजी गई थी? यह लेख समझाता है कि यह सत्यापन वास्तव में कैसे काम करता है: HMAC किन चीज़ों को कवर करता है, इसे पार्स किए गए ऑब्जेक्ट के बजाय कच्चे बॉडी पर क्यों जांचना चाहिए, तुलना स्थिर समय में क्यों चलनी चाहिए, टाइमस्टैम्प रीप्ले हमलों को कैसे सीमित करते हैं, और हर प्रदाता हेडर को अलग ढंग से क्यों प्रारूपित करता है। यदि आप पहले कोई असली डिलीवरी देखना चाहते हैं — हेडर, हस्ताक्षर और कच्चा बॉडी एक साथ — तो किसी भेजने वाले को Webhook टेस्टर की ओर इंगित करें और जो आता है उसका निरीक्षण करें।

गुप्त URL प्रमाणीकरण क्यों नहीं है

सबसे पुरानी Webhook सुरक्षा एक अनुमान न लगाई जा सकने वाली URL है: एक लंबा यादृच्छिक पथ खंड जिसे केवल भेजने वाली सेवा जानती है। यह पासवर्ड जैसा लगता है, लेकिन इसका व्यवहार किसी पते जैसा है, और पते लीक होते हैं। वह URL एक्सेस लॉग, प्रॉक्सी लॉग, मॉनिटरिंग डैशबोर्ड, बग रिपोर्टों के स्क्रीनशॉट और भेजने वाली सेवा के अपने कॉन्फ़िगरेशन पृष्ठों में लिखी जाती है, जहाँ कोई भी सहायता इंजीनियर — या जो कोई उनमें से किसी एक प्रणाली से समझौता कर ले — उसे पढ़ सकता है। पासवर्ड को आराम की स्थिति में हैश किया जा सकता है और प्रति उपयोगकर्ता बदला जा सकता है; URL हर उस जगह शब्दशः कॉपी होती है जहाँ उसका उपयोग होता है, और हर प्रति एक स्थायी निमंत्रण होती है।

गहरी समस्या वह है जो एंडपॉइंट किसी अनुरोध के पहुँचने पर उसके साथ करता है। Webhook हैंडलर चालानों को भुगतान किया हुआ चिह्नित करते हैं, खाते बनाते हैं, लाइसेंस देते हैं, डेटा हटाते हैं। ये ऐसी कार्रवाइयाँ हैं जिनकी जालसाज़ी करना सार्थक है, और एक जाली अनुरोध की लागत हमलावर के लिए इंटरनेट पर कहीं से भी एक HTTP POST है। TLS यहाँ मदद नहीं करता, क्योंकि TLS जुड़ने वाले के सामने आपके सर्वर को प्रमाणित करता है; जुड़ने वाला पक्ष कौन है, इस बारे में वह कुछ नहीं कहता। हस्ताक्षर भरोसे को “नॉक करना कहाँ है, यह जानने” से हटाकर “ऐसा रहस्य जानने” पर ले जाता है जो स्वयं अनुरोध में कभी दिखाई नहीं देता, और यह श्रेणीगत रूप से मज़बूत दावा है।

HMAC हस्ताक्षर वास्तव में क्या साबित करता है

भेजने वाला और प्राप्त करने वाला एक रहस्य साझा करते हैं, जो एंडपॉइंट पंजीकृत होते समय उत्पन्न होता है। हर डिलीवरी के लिए, भेजने वाला बाइट्स के एक परिभाषित समूह पर HMAC — एक कुंजी-युक्त हैश — की गणना करता है: आमतौर पर कच्चा अनुरोध बॉडी, अक्सर उससे पहले एक टाइमस्टैम्प, कभी-कभी एक डिलीवरी ID के साथ। परिणाम एक हेडर में यात्रा करता है। प्राप्त करने वाला प्राप्त बाइट्स पर अपने रहस्य की प्रति के साथ HMAC की पुनर्गणना करता है और दोनों मानों की तुलना करता है। मेल खाने से एक साथ दो चीज़ें साबित होती हैं: भेजने वाले के पास रहस्य था, और कवर किए गए बाइट्स बिल्कुल वही बाइट्स हैं जिन पर भेजने वाले ने हस्ताक्षर किए थे। राशि फ़ील्ड का एक अक्षर बदल दीजिए और पुनर्गणना किया गया मान पूरी तरह अलग हो जाएगा।

दो सीमाएँ स्पष्ट रूप से कहना उचित है। पहली, हस्ताक्षर एन्क्रिप्शन नहीं है: बॉडी नेटवर्क को उस हर व्यक्ति के लिए पठनीय रूप में पार करती है जो उसे देख सकने की स्थिति में है — उसे निजी रखना TLS का काम है — और हस्ताक्षर केवल मूल और अखंडता की गारंटी देता है। दूसरी, बॉडी का साधारण हैश कुछ भी साबित नहीं करेगा, क्योंकि कोई भी किसी भी चीज़ का हैश बना सकता है। रहस्य ही वह है जो हैश को संदेश प्रमाणीकरण कोड में बदल देता है। इस साइट का हैश जेनरेटर बिना कुंजी के हैश गणना करता है, जो चेकसम के रूप में ठीक हैं — लेकिन ऐसा चेकसम जिसे कोई भी पुनः गणना कर सकता है, किसी को प्रमाणित नहीं करता।

कच्चे बॉडी का, बाइट दर बाइट, सत्यापन करें

“असली डिलीवरियों पर सत्यापन विफल” का सबसे आम कारण कोई हमलावर नहीं है — वह प्राप्त करने वाले का अपना फ्रेमवर्क है। हस्ताक्षर की गणना बॉडी पर ठीक उसी रूप में की गई थी जिस रूप में वह प्रसारित हुई थी: यह कुंजी क्रम, यह रिक्त स्थान, ये एस्केप किए गए अक्षर, यह संख्या प्रारूपण। अधिकांश वेब फ्रेमवर्क आपके हैंडलर के चलने से पहले JSON बॉडी को मददगार ढंग से एक ऑब्जेक्ट में पार्स कर देते हैं, और यदि आप सत्यापन के लिए उस ऑब्जेक्ट को फिर से सीरियलाइज़ करते हैं, तो आपको वही डेटा अलग बाइट्स में मिलता है: कुंजियाँ पुनः क्रमित, रिक्त स्थान जुड़े हुए, 1e3 का 1000 के रूप में प्रतिपादन, यूनिकोड एस्केप किया हुआ या बिना एस्केप का। अर्थ की दृष्टि से समान, क्रिप्टोग्राफ़ी की दृष्टि से भिन्न — और सत्यापन उस डिलीवरी पर विफल हो जाता है जो पूरी तरह असली थी। Stripe का दस्तावेज़ नियम को सीधे-सीधे कहता है: हस्ताक्षर सत्यापन के लिए कच्चा बॉडी आवश्यक है, और फ्रेमवर्क को उसे छेड़ना नहीं चाहिए।

परिचालन परिणाम क्रम का है: किसी भी चीज़ के पार्स करने से पहले कच्चे अनुरोध बॉडी को पकड़ें, उन बाइट्स के विरुद्ध हस्ताक्षर सत्यापित करें, और तभी पार्स करके प्रसंस्करण करें। बॉडी को फिर से लिखने वाले प्रॉक्सी और मिडलवेयर — पुनः एन्कोडिंग, सामान्यीकरण, सुंदर स्वरूपण — हस्ताक्षरों को उतनी ही निश्चितता से तोड़ते हैं जितनी कोई पार्सर, इसीलिए डीबगिंग अनुरोध को ठीक उसी रूप में देखने से शुरू होती है जिस रूप में वह आया था। प्राप्त करने वाला उपकरण आपको ठीक यही दृश्य देता है: जब कोई डिलीवरी Webhook टेस्टर में उतरती है, तो आप जिसका निरीक्षण करते हैं वह अछूता अनुरोध होता है, हेडर और बॉडी जैसे प्रसारित हुए थे, जिसके विरुद्ध किसी हस्ताक्षर को ईमानदारी से जांचा जा सकता है।

स्थिर-समय तुलना, और घड़ी हस्ताक्षर का हिस्सा क्यों है

दो HMAC मानों की तुलना मामूली चरण लगती है, और यह एक सूक्ष्म रिसाव छिपाती है। साधारण स्ट्रिंग तुलना पहले न मिलने वाले अक्षर पर रुक जाती है, इसलिए जब कोई अनुमान जल्दी गलत होता है तो वह तेज़ी से लौटती है और जब कोई लंबा उपसर्ग संयोग से सही होता है तो धीरे लौटती है। कई अनुरोधों के दौरान, वह समय-अंतर हमलावर को रहस्य जाने बिना, एक-एक अक्षर मापते हुए वैध हस्ताक्षर की ओर बढ़ने देता है। स्थिर-समय तुलना हमेशा पूरी लंबाई की जांच करती है और उतना ही समय लेती है, चाहे मान पहले अक्षर में भिन्न हों या अंतिम में। प्लेटफ़ॉर्म दस्तावेज़ इस बारे में स्पष्ट हैं — उदाहरण के लिए GitHub का दस्तावेज़ साधारण समानता ऑपरेटर के विरुद्ध स्पष्ट चेतावनी देता है और crypto.timingSafeEqual जैसे स्थिर-समय फ़ंक्शनों की ओर इशारा करता है। लाइब्रेरी फ़ंक्शन का उपयोग करें; तुलना लूप स्वयं न लिखें।

ताज़गी तीसरा गुण है, और केवल हस्ताक्षर आपको वह नहीं दे सकता: पकड़ी गई असली डिलीवरी, शब्दशः दोबारा भेजी गई, पूरी तरह वैध हस्ताक्षर लिए होती है। इसीलिए कई योजनाएँ टाइमस्टैम्प को हस्ताक्षरित बाइट्स में मोड़ देती हैं, और इसीलिए प्राप्त करने वाले उन डिलीवरियों को अस्वीकार करते हैं जिनका टाइमस्टैम्प सहनशीलता खिड़की से बाहर बैठता है — Stripe की योजना में, उसके दस्तावेज़ के अनुसार, डिफ़ॉल्ट रूप से पाँच मिनट। खिड़की एक सोचा-समझा समझौता है: घड़ी के विस्थापन और धीमी कतारों को सोखने के लिए पर्याप्त चौड़ी, और इतनी संकीर्ण कि पकड़ा गया अनुरोध जल्दी पुराना हो जाए। खिड़की के अंदर, बचाव इडेम्पोटेंसी पर आ जाता है: जो कुछ भी आपने प्रसंस्कृत किया है उसकी डिलीवरी या घटना ID दर्ज करें, और दोहराव छोड़ दें — जिसकी आपको वैसे भी ज़रूरत है, क्योंकि ईमानदार भेजने वाले फिर प्रयास करते हैं, और फिर प्रयास अच्छे इरादों वाला रीप्ले होता है।

हर प्रदाता का हेडर अलग क्यों दिखता है

यांत्रिकी मानक है; पैकेजिंग कभी एक नहीं हुई। Stripe t=<timestamp>,v1=<signature> के रूप का Stripe-Signature हेडर भेजता है, और एक साथ कई v1 मान शामिल कर सकता है। GitHub X-Hub-Signature-256 भेजता है, जिसका मान एल्गोरिदम-उपसर्ग वाली स्ट्रिंग sha256=<hex digest> होता है — कोई टाइमस्टैम्प नहीं, इसलिए GitHub डिलीवरी में कोई अंतर्निहित समाप्ति नहीं होती, और रीप्ले बचाव पूरी तरह डिलीवरी ID के दोहराव हटाने पर टिका होता है। Standard Webhooks विनिर्देश इस प्रश्न को सुलझाने का प्रयास है: डिलीवरी ID, टाइमस्टैम्प और हस्ताक्षर के लिए अलग-अलग हेडर, जिसमें हस्ताक्षर तीनों के साथ बॉडी पर गणना किया जाता है, ताकि नए प्रदाता असंगत रूपांतरों का आविष्कार करना बंद कर दें। अंतर ऐतिहासिक संयोग हैं, अलग सुरक्षा मॉडल नहीं — हर योजना में अपरिवर्ती एक ही है: साझा रहस्य, कवर किए गए बाइट्स का परिभाषित समूह, स्थिर-समय तुलना।

Stripe के प्रारूप में कई v1 मान उस कार्य के लिए मौजूद हैं जिसका सामना हर प्राप्त करने वाला अंततः करता है: रहस्य को बदलना। भोले ढंग से किया जाए — रहस्य बदलो, तैनात करो, आशा करो — तो बदलाव उड़ान में मौजूद हर चीज़ को तोड़ देता है, क्योंकि पुराने रहस्य से हस्ताक्षरित पुनः प्रयास और कतारबद्ध डिलीवरियाँ अचानक विफल हो जाती हैं। काम करने वाला पैटर्न एक समानांतर खिड़की है: अतिव्यापन के दौरान, पुराने और नए दोनों रहस्यों के अंतर्गत हस्ताक्षर स्वीकार किए जाते हैं, आप पुष्टि करते हैं कि डिलीवरियाँ नए रहस्य के अंतर्गत सत्यापित होती हैं, और तभी पुराना रहस्य सेवानिवृत्त होता है। वही खिड़की पैटर्न कुंजी ID और अतिव्यापी कुंजियों की JWT दुनिया में दिखाई देता है, और जब टीमें उसे छोड़ देती हैं तो विफलता का ढंग भी वही होता है, जैसा JWT गलतियों वाला लेख वर्णन करता है। ऐसा रहस्य जिसे बिना ठहराव के बदला न जा सके, तब भी नहीं बदला जाएगा जब बदला जाना चाहिए — उस दिन भी जब वह लीक होता है — इसीलिए असाधारण न लगने वाली समानांतर खिड़की प्रोटोकॉल का हिस्सा है, बाद का विचार नहीं।

स्रोत और आगे की पठन सामग्री

अक्सर पूछे जाने वाले प्रश्न

जिन डिलीवरियों को मैं असली जानता हूँ, उन पर सत्यापन विफल हो जाता है। सबसे पहले कहाँ देखूँ?

बॉडी पर। क्रम से जांचें: क्या आप प्राप्त हुए कच्चे बाइट्स का सत्यापन कर रहे हैं, या पार्स करके फिर से सीरियलाइज़ किए गए संस्करण का? क्या आप इस एंडपॉइंट और इस मोड के लिए रहस्य का उपयोग कर रहे हैं — परीक्षण और लाइव एंडपॉइंट के रहस्य अलग होते हैं, और प्रत्येक पंजीकृत एंडपॉइंट का अपना होता है? क्या भेजने वाले और आपके हैंडलर के बीच कुछ — प्रॉक्सी, फ्रेमवर्क मिडलवेयर — बॉडी को फिर से लिख रहा है? एक डिलीवरी को अछूती पकड़ना, उदाहरण के लिए Webhook टेस्टर में, और उन ठीक-ठीक बाइट्स के विरुद्ध सत्यापित करना, “भेजने वाला अजीब ढंग से हस्ताक्षर करता है” को “मेरा स्टैक बाइट्स बदल देता है” से एक ही चरण में अलग कर देता है।

हम पहले से HTTPS का उपयोग करते हैं। क्या हस्ताक्षर अनावश्यक नहीं है?

नहीं — दोनों अलग-अलग दिशाओं को प्रमाणित करते हैं। TLS भेजने वाले को साबित करता है कि वह आपके असली सर्वर तक पहुँचा, और पारगमन में बॉडी को एन्क्रिप्ट करता है। वह इस पर कोई शर्त नहीं रखता कि कौन जुड़ता है: इंटरनेट पर कोई भी आपके एंडपॉइंट से पूरी तरह वैध TLS कनेक्शन खोल सकता है और जाली “भुगतान सफल” बॉडी POST कर सकता है। हस्ताक्षर वह हिस्सा है जो भेजने वाले और सामग्री को प्रमाणित करता है। TLS डिलीवरी को निजी रखता है; हस्ताक्षर उसे भरोसेमंद बनाता है।

Webhook रहस्य को कितनी बार बदलना चाहिए?

तुरंत, उसके लीक होने के किसी भी संदेह पर — वह डैशबोर्ड, पर्यावरण फ़ाइलों और CI चरों में रहता है, इसलिए उजागर होने को समय की बात मानें। नियमित रूप से, जिस भी लय पर आपकी टीम तुलनीय क्रेडेंशियल बदलती है, बशर्ते बदलाव सस्ता हो: दोहरी-स्वीकृति खिड़की के साथ यह एक घटना नहीं, कॉन्फ़िगरेशन परिवर्तन होता है। यदि किसी प्रदाता की योजना बदलाव को पीड़ादायक बनाती है, तो एकीकरण चुनते समय उसका मूल्यांकन करें और Standard Webhooks जैसी प्रकाशित योजनाओं को प्राथमिकता दें — बदल सकने की क्षमता प्रोटोकॉल का सुरक्षा गुण है, कोई सज्जा नहीं।