ডকুমেন্টেশন

নিরাপত্তা ও HMAC স্বাক্ষর

Savebase-এর পাঠানো প্রতিটি webhook অনুরোধ HMAC-SHA256 দিয়ে স্বাক্ষরিত হয়, যাতে নিশ্চিত হতে পারেন এটি সত্যিই Savebase থেকে এসেছে এবং পথে পরিবর্তন করা হয়নি। webhook ইভেন্ট প্রক্রিয়াকরণের আগে সব সময় এই স্বাক্ষর যাচাই করা উচিত।

স্বাক্ষর যেভাবে কাজ করে

Savebase আপনার ওয়েবহুকের নিম্নলিখিত মানকে কী হিসেবে ব্যবহার করে কাঁচা রিকোয়েস্ট বডির ওপর HMAC-SHA256 চালিয়ে স্বাক্ষর গণনা করে: স্বাক্ষরের গোপন কী কী হিসেবে। ফল পাঠানো হয় X-Savebase-Signature হেডার হিসেবে sha256=<hex-digest>.

কাঁচা বডি ব্যবহার

সবসময় HMAC গণনা করুন এর ওপর: অনুরোধের মূল বডির অপরিবর্তিত বাইট — নতুন করে সিরিয়ালাইজ করা JSON অবজেক্ট নয়। JSON পার্স করে আবার স্ট্রিং বানালে ফাঁক ও কী-এর ক্রম বদলে স্বাক্ষর যাচাই ব্যর্থ হতে পারে।
হেডারমানের ফরম্যাট
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 ১০ সেকেন্ডের মধ্যে। Savebase ১০ সেকেন্ড পর টাইমআউট করে এবং ডেলিভারি ব্যর্থ হিসেবে চিহ্নিত করে। ভারী কাজ ব্যাকগ্রাউন্ড জবে পাঠান।
  • স্বাক্ষরের গোপন কী কখনো লগ করবেন না। পাসওয়ার্ডের মতো রাখুন। বদলান এখানে Settings → Webhooks আপনার মনে হলে এটি অন্যের হাতে পড়েছে।
  • শুধু HTTPS ব্যবহার করুন। Savebase সাধারণ HTTP এন্ডপয়েন্ট প্রত্যাখ্যান করে।
  • টাইমিং-সেফ তুলনা করুন। সবসময় ব্যবহার করুন crypto.timingSafeEqual (Node), hmac.compare_digest (Python), অথবা hash_equals (PHP) ব্যবহার করুন — সরাসরি তুলনা করতে কখনো ব্যবহার করবেন না ===.
  • ব্যর্থতা নিজেই সামলান। Savebase ডেলিভারির ফল রাখে, কিন্তু বর্তমান ব্যবস্থায় ব্যর্থ রিকোয়েস্ট স্বয়ংক্রিয় পুনঃচেষ্টা বা পুনরায় চালানো হয় না।