SocialHook
الوثائق/الأمان والتوقيع

الأمان وتوقيع الـ Payload

كل payload في SocialHook موقّع بـ HMAC-SHA256 باستخدام مفتاحك السري. يجب عليك التحقق من هذا التوقيع على خادمك قبل معالجة أي رسالة — هذا يحميك من الـ payloads المزيفة.

كيف يعمل التوقيع

عندما يُسلَّم SocialHook payload إلى رابط webhook الخاص بك، يحسب تجزئة HMAC-SHA256 لـ جسم الطلب الخام باستخدام مفتاحك السري. تُرسَل هذه التجزئة في رأس HTTP X-SocialHook-Signature بتنسيق sha256=<hex_digest>.

على خادمك، تحسب نفس التجزئة باستخدام نفس المفتاح السري والجسم الخام — ثم تقارن القيمتين. إذا تطابقتا، فالـ payload أصيل.

لا تتخطَّ التحقق من التوقيع أبدًا. بدونه، يمكن لأي شخص يعرف رابط webhook الخاص بك إرسال payloads مزيفة إلى خادمك.

التحقق — Node.js

Node.js / Express
const crypto = require('crypto')

function verifySocialHookSignature(req, secret) {
  const header = req.headers['x-socialhook-signature']
  if (!header) return false

  const hash = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(req.rawBody) // must be raw Buffer
    .digest('hex')

  // Use timingSafeEqual to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(header)
  )
}

// In your Express middleware:
app.use(express.json({
  verify: (req, _, buf) => { req.rawBody = buf }
}))

app.post('/webhook', (req, res) => {
  if (!verifySocialHookSignature(req, process.env.SOCIALHOOK_SECRET)) {
    return res.status(401).send('Invalid signature')
  }
  // Safe to process
  res.sendStatus(200)
})

التحقق — Python

Python / Flask
import hmac
import hashlib
from flask import Flask, request, abort
import os

app = Flask(__name__)

def verify_signature(payload_body, signature_header, secret):
  if not signature_header:
    return False
  expected = 'sha256=' + hmac.new(
    secret.encode(),
    payload_body,
    hashlib.sha256
  ).hexdigest()
  return hmac.compare_digest(expected, signature_header)

@app.route('/webhook', methods=['POST'])
def webhook():
  sig = request.headers.get('X-SocialHook-Signature')
  if not verify_signature(request.data, sig, os.environ['SOCIALHOOK_SECRET']):
    abort(401)
  # Safe to process request.json
  return '', 200

التحقق — PHP

PHP
<?php
function verifySocialHookSignature($rawBody, $signatureHeader, $secret): bool {
  if (empty($signatureHeader)) return false;
  $expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
  return hash_equals($expected, $signatureHeader);
}

$rawBody = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_SOCIALHOOK_SIGNATURE'] ?? '';

if (!verifySocialHookSignature($rawBody, $sig, getenv('SOCIALHOOK_SECRET'))) {
  http_response_code(401);
  exit('Invalid signature');
}
$payload = json_decode($rawBody, true);
// Safe to process $payload
http_response_code(200);

أفضل ممارسات الأمان

استخدم timingSafeEqual دائمًا — مقارنة السلاسل البسيطة عرضة لهجمات التوقيت. استخدم crypto.timingSafeEqual() (Node) أو hmac.compare_digest() (Python) أو hash_equals() (PHP).
وقّع الجسم الخام وليس JSON المحلل — احسب HMAC على بايتات جسم الطلب الخام قبل أي تحليل JSON. قد تنتج محللو JSON المختلفون تسلسلات بايت مختلفة.
خزّن مفتاحك السري كمتغير بيئة — لا تُضمّنه أبدًا في الكود المصدري ولا تُلزمه في مستودع.
أعد 200 قبل المعالجة — ينتظر SocialHook حتى 10 ثوانٍ للرد. أعد 200 فورًا وعالج الـ payload بشكل غير متزامن.
استخدم HTTPS فقط — لن يُسلَّم SocialHook إلا لنقاط نهاية HTTPS. نقاط HTTP العادية تُرفض عند الإعداد.
طبّق التعامل مع التكرار — في حالات نادرة قد يُسلَّم SocialHook نفس الحدث مرتين. خزّن قيم delivery_id وتخطَّ المكررات.
دوّر مفتاحك السري دوريًا — أنشئ مفتاحًا سريًا جديدًا من إعدادات لوحة التحكم وحدّث متغير البيئة على خادمك.
وجدت ثغرة أمنية؟ يرجى الإبلاغ عنها بمسؤولية إلى security@socialhook.io. لا تكشف عنها علنًا قبل أن تتاح لنا فرصة معالجتها. نرد خلال 72 ساعة.