SMLLR
होमगाइडवेबहुक सिग्नेचर वेरिफिकेशन: अपना SMLLR एंडपॉइंट सुरक्षित करना

वेबहुक सिग्नेचर वेरिफिकेशन: अपना SMLLR एंडपॉइंट सुरक्षित करना

यह कैसे वेरीफाई करें कि एक वेबहुक रिक्वेस्ट असल में SMLLR से आई थी — सटीक हेडर फॉर्मेट, HMAC-SHA256 फॉर्मूला, और एक जेनरिक एंडपॉइंट के लिए एक वर्किंग Node.js कोड सैंपल।

छोटा जवाब

हर जेनरिक SMLLR वेबहुक डिलीवरी में sha256= के तौर पर फॉर्मेटेड एक X-Smllr-Signature हेडर शामिल है, X-Smllr-Timestamp और X-Smllr-Event हेडर के साथ। HMAC को HMAC-SHA256(secret, "${timestamp}.${body}") के तौर पर कैलकुलेट किया जाता है, प्रति-एंडपॉइंट सीक्रेट इस्तेमाल करते हुए जो Settings → Webhooks & Slack में एंडपॉइंट बनाते समय एक बार दिखाया गया था। अपने सर्वर पर रॉ रिक्वेस्ट बॉडी और टाइमस्टैंप हेडर इस्तेमाल करके वही HMAC दोबारा कैलकुलेट करें, फिर इसे आपको मिले सिग्नेचर से तुलना करें — अगर वे मैच करते हैं, रिक्वेस्ट SMLLR से आई और ट्रांज़िट में इसके साथ छेड़छाड़ नहीं हुई। Slack एंडपॉइंट इसे पूरी तरह छोड़ देते हैं: एक Slack Incoming Webhook URL खुद सीक्रेट है, इसलिए Slack डिलीवरी पर कोई सिग्नेचर हेडर नहीं है।

बिल्कुल वेरीफाई क्यों करें

एक वेबहुक एंडपॉइंट एक पब्लिक URL है — जो कोई भी इसे अनुमान लगाए या खोजे वह इस पर एक बनावटी पेलोड POST कर सकता है, और वेरिफिकेशन के बिना आपके सर्वर के पास एक असली SMLLR इवेंट को एक जाली इवेंट से अलग करने का कोई तरीका नहीं है। ज़्यादातर यूज़ केस के लिए दांव मध्यम हैं (एक नकली scan.created इवेंट सिर्फ एनालिटिक्स को दूषित करता है), लेकिन किसी भी ऐसी चीज़ के लिए जो एक असली-दुनिया की एक्शन ट्रिगर करती है — qr.scan_limit_reached पर इन्वेंटरी रीस्टॉक करना, एक टीम चैनल को सूचित करना, एक डेटाबेस में लिखना जिस पर आपके दूसरे सिस्टम भरोसा करते हैं — एक अनवेरिफाइड एंडपॉइंट का मतलब है कोई भी मांग पर वह एक्शन ट्रिगर कर सकता है। सिग्नेचर वेरिफिकेशन 'इस URL पर एक रिक्वेस्ट पहुंची' और 'एक रिक्वेस्ट जो SMLLR ने असल में भेजी वह इस URL पर पहुंची' के बीच का फर्क है।

सटीक हेडर फॉर्मेट

हर जेनरिक एंडपॉइंट डिलीवरी के साथ तीन हेडर आते हैं:

  • **`X-Smllr-Signature`** — `sha256=<hex-encoded HMAC>`, वह वैल्यू जिसे आप दोबारा कैलकुलेट करेंगे और तुलना करेंगे।
  • **`X-Smllr-Timestamp`** — Unix टाइमस्टैंप (सेकंड) जिस पल SMLLR ने रिक्वेस्ट भेजी, और साइन्ड स्ट्रिंग के अंदर इस्तेमाल की गई वही वैल्यू।
  • **`X-Smllr-Event`** — इस डिलीवरी के लिए इवेंट टाइप (जैसे `scan.created`), बॉडी पार्स करने से पहले भी रूटिंग के लिए उपयोगी।

साइनिंग फॉर्मूला

सिग्नेचर एक सिंगल कॉन्कैटिनेटेड स्ट्रिंग का HMAC-SHA256 है — टाइमस्टैंप, एक लिटरल पीरियड, और रॉ रिक्वेस्ट बॉडी — आपके एंडपॉइंट के सीक्रेट से की किया गया:

signature = HMAC-SHA256(secret, `${timestamp}.${body}`)

व्यवहार में मायने रखने वाली कुछ डिटेल: body रॉ, अनपार्स्ड रिक्वेस्ट बॉडी होनी चाहिए — अगर आपका फ्रेमवर्क रॉ बाइट्स कैप्चर करने से पहले अपने आप JSON पार्स कर देता है, पार्स्ड ऑब्जेक्ट को दोबारा स्ट्रिंगिफाई करना भरोसे से बाइट-फॉर-बाइट वही स्ट्रिंग रीप्रोड्यूस नहीं करेगा जो SMLLR ने साइन की थी (की ऑर्डर, व्हाइटस्पेस, और नंबर फॉर्मेटिंग सभी अलग हो सकते हैं)। और टाइमस्टैंप साइन्ड स्ट्रिंग का हिस्सा है, सिर्फ एक साइडकार वैल्यू नहीं — यह खास तौर पर इसलिए है ताकि एक कैप्चर्ड, वैलिड पेलोड को घंटों बाद चुपचाप रीप्ले न किया जा सके बिना आपके पास इसे नोटिस करने का कोई तरीका हुए।

Node.js में इसे वेरीफाई करना

एक मिनिमल Express उदाहरण, रॉ बॉडी इस्तेमाल करते हुए (express.json() के बजाय express.raw() के ज़रिए कैप्चर किया गया, ताकि req.body एक Buffer हो, पहले से पार्स्ड न हो):

const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/webhooks/smllr', express.raw({ type: 'application/json' }), (req, res) => {
  const signatureHeader = req.get('X-Smllr-Signature') || '';
  const timestamp = req.get('X-Smllr-Timestamp') || '';
  const rawBody = req.body.toString('utf8');

  const expected = crypto
    .createHmac('sha256', process.env.SMLLR_WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const received = signatureHeader.replace('sha256=', '');

  const isValid =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(rawBody);
  // handle event.type / event.data here
  res.status(200).send('ok');
});

दो चीज़ें बताने लायक हैं: crypto.timingSafeEqual का इस्तेमाल === के बजाय खास तौर पर एक टाइमिंग साइड-चैनल से बचने के लिए किया जाता है जो थ्योरेटिकली एक बार में एक बाइट सही सिग्नेचर के बारे में जानकारी लीक कर सकता है, और इसे पास किए गए दोनों बफर पहले वही लंबाई के होने चाहिए (असमान लंबाई की एक सिंपल स्ट्रिंग तुलना एरर थ्रो करेगी)। SMLLR_WEBHOOK_SECRET को एक एनवायरनमेंट वेरिएबल के तौर पर या एक सीक्रेट्स मैनेजर में स्टोर करें — इसे कभी एप्लिकेशन कोड के साथ कमिट न करें।

Slack अपवाद

Slack एंडपॉइंट ऊपर बताई गई कोई भी चीज़ नहीं ले जाते — कोई X-Smllr-Signature नहीं, कोई HMAC नहीं, वेरीफाई करने के लिए कुछ नहीं। ऐसा इसलिए है क्योंकि डिलीवरी मैकेनिज्म अलग है: SMLLR एक Slack Incoming Webhook URL पर POST कर रहा है, और URL खुद (https://hooks.slack.com/services/...) क्रेडेंशियल का काम करता है — Slack उस बिल्कुल सही URL पर किसी भी रिक्वेस्ट को अधिकृत मानता है। इस लूप में वेरीफाई करने के लिए आपका कोई सर्वर नहीं है, इसलिए Incoming Webhook URL को वैसे ही ट्रीट करें जैसे आप एक API की को करेंगे: इसे पब्लिकली पोस्ट न करें, इसे एक पब्लिक रिपो में कमिट न करें, और अगर आपको शक हो कि यह लीक हो गया है तो इसे Slack में रीजेनरेट करें।

इसकी कीमत क्या है

सिग्नेचर वेरिफिकेशन SMLLR के Pro प्लान (₹4,999/महीना) और ऊपर के हर जेनरिक वेबहुक एंडपॉइंट की एक प्रॉपर्टी है — साइन्ड पेलोड पाने के लिए कोई अलग चार्ज या हायर टियर ज़रूरी नहीं है; हर जेनरिक एंडपॉइंट को अपने आप एक मिलता है। एंडपॉइंट सेटअप के लिए खुद, How to Set Up SMLLR Webhooks देखें।

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

SMLLR एक वेबहुक पेलोड साइन करने के लिए सटीक फॉर्मूला क्या इस्तेमाल करता है?

HMAC-SHA256(secret, ${timestamp}.${body}), जहां secret प्रति-एंडपॉइंट साइनिंग सीक्रेट है, timestamp X-Smllr-Timestamp हेडर में Unix टाइमस्टैंप है, और body रॉ, अनपार्स्ड रिक्वेस्ट बॉडी है।

मुझे अपने एंडपॉइंट का साइनिंग सीक्रेट कहां मिलता है?

यह Settings → Webhooks & Slack में एक जेनरिक एंडपॉइंट बनाते समय अपने आप जेनरेट होता है, और क्रिएशन के समय एक बार दिखाया जाता है — इसे सुरक्षित रूप से स्टोर करें, क्योंकि यह उसके बाद फिर से नहीं दिखाया जाता।

मेरी सिग्नेचर वेरिफिकेशन सही सीक्रेट इस्तेमाल करने के बावजूद मैच क्यों नहीं करती?

सबसे आम वजह है वह सटीक रॉ बाइट्स भेजने के बजाय बॉडी का एक दोबारा-सीरियलाइज़्ड/पार्स्ड वर्ज़न साइन करना जो SMLLR ने भेजे — किसी भी JSON पार्सिंग मिडलवेयर के इसे छूने से पहले रॉ रिक्वेस्ट बॉडी कैप्चर करें।

क्या Slack वेबहुक एंडपॉइंट में वेरीफाई करने के लिए एक सिग्नेचर है?

नहीं। एक Slack Incoming Webhook URL खुद सीक्रेट है — Slack डिलीवरी पर कोई अलग सिग्नेचर हेडर नहीं है।

एक सामान्य स्ट्रिंग तुलना के बजाय crypto.timingSafeEqual का इस्तेमाल क्यों करें?

एक सामान्य तुलना इस बारे में टाइमिंग जानकारी लीक कर सकती है कि कितने शुरुआती कैरेक्टर मैच हुए, जो एक थ्योरेटिकल साइड-चैनल है; timingSafeEqual पहले मिसमैच के होने की जगह की परवाह किए बिना कॉन्स्टेंट टाइम में तुलना करता है।

क्या टाइमस्टैंप हेडर सिर्फ जानकारी के लिए है, या यह सिक्योरिटी के लिए मायने रखता है?

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

साइन्ड वेबहुक के लिए मुझे कौन सा प्लान चाहिए?

आमतौर पर वेबहुक जैसी ही Pro प्लान (₹4,999/महीना) या ऊपर की ज़रूरत — उस प्लान पर हर जेनरिक एंडपॉइंट को अपने आप साइन्ड डिलीवरी मिलती है।

संबंधित रिसोर्सेज

सभी गाइड देखें