दस्तावेज़

सुरक्षा और HMAC साइनिंग

Savebase के भेजे हर वेबहुक अनुरोध पर HMAC-SHA256 हस्ताक्षर होता है, जिससे आप पुष्टि कर सकते हैं कि वह वास्तव में Savebase से आया है और रास्ते में बदला नहीं गया। वेबहुक इवेंट प्रोसेस करने से पहले यह हस्ताक्षर हमेशा सत्यापित करें।

हस्ताक्षर कैसे काम करता है

Savebase आपके वेबहुक के इस मान का उपयोग करके अनुरोध के मूल बॉडी पर HMAC-SHA256 चलाता है और हस्ताक्षर निकालता है: साइनिंग सीक्रेट कुंजी के रूप में। परिणाम इसमें जाता है: X-Savebase-Signature header में इस रूप में sha256=<hex-digest>.

कच्चा body इस्तेमाल करें

HMAC की गणना हमेशा इस पर करें: कच्चे अपरिवर्तित request body bytes — फिर serialise किया JSON नहीं। parse और stringify से space व key क्रम बदलकर हस्ताक्षर असफल हो सकता है।
हेडरमान का प्रारूप
X-Savebase-Signaturesha256=abcdef0123...
X-Savebase-Eventpost.saved
User-AgentSavebase-Webhook/1.0

Node.js / Express

bash
const crypto = require('crypto');

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'utf8'),
    Buffer.from(signatureHeader, 'utf8')
  );
}

app.post(
  '/savebase-webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const sig = req.headers['x-savebase-signature'];
    if (!sig || !verifySignature(req.body, sig, process.env.SAVEBASE_WEBHOOK_SECRET)) {
      return res.status(401).send('Invalid signature');
    }
    const event = JSON.parse(req.body);
    console.log('Event:', event.event, event.data.id);
    res.sendStatus(200);
  }
);

Python / Flask

bash
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = os.environ['SAVEBASE_WEBHOOK_SECRET']

@app.route('/savebase-webhook', methods=['POST'])
def handle_webhook():
    sig_header = request.headers.get('X-Savebase-Signature', '')
    raw_body = request.get_data()

    expected = 'sha256=' + hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, sig_header):
        abort(401)

    payload = request.get_json()
    print('Received event:', payload['event'])
    return '', 200

PHP

bash
<?php
$secret  = getenv('SAVEBASE_WEBHOOK_SECRET');
$rawBody = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_X_SAVEBASE_SIGNATURE'] ?? '';
$expected  = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);

if (!hash_equals($expected, $sigHeader)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($rawBody, true);
error_log('Received: ' . $event['event']);
http_response_code(200);

सुरक्षा के सर्वोत्तम तरीके

  • जल्दी जवाब दें। लौटाएँ: 200 OK 10 सेकंड में। फिर Savebase असफल बताता है। भारी काम बैकग्राउंड में करें।
  • अपना हस्ताक्षर सीक्रेट कभी लॉग में न लिखें। इसे पासवर्ड जैसा मानें। इसे यहाँ बदलें: Settings → Webhooks यदि लगे कि चोरी हुआ।
  • केवल HTTPS इस्तेमाल करें। Savebase साधारण HTTP एंडपॉइंट अस्वीकार करता है।
  • टाइमिंग-सुरक्षित तुलना इस्तेमाल करें। हमेशा इस्तेमाल करें crypto.timingSafeEqual (Node), hmac.compare_digest (Python), या hash_equals (PHP) — कभी भी साधारण ===.
  • विफलताओं को खुद सँभालें। Savebase डिलीवरी का नतीजा दर्ज करता है, लेकिन मौजूदा व्यवस्था विफल अनुरोधों को अपने-आप दोबारा नहीं आज़माती और न ही रीप्ले करती है।