انتقل إلى المحتوى الرئيسي

التحقق من التواقيع

كل ويب هوك ترسله TakeTheme موقّع بـمفتاح التوقيع الخاص بالعنوان (whsec_…). والتحقق من التوقيع يثبت أن الطلب صادر فعلًا عن TakeTheme وأن الحمولة لم تُعبَث بها أثناء النقل. تحقّق دائمًا قبل التصرف بناءً على الحمولة — وإلا استطاع كل من يكتشف رابطك تزوير الأحداث.

ترويسة التوقيع

يحمل كل تسليم ترويسة X-TakeTheme-Signature:

X-TakeTheme-Signature: t=1751371200,v1=5257a869e7b...

وتتكوّن من حقلين مفصولين بفاصلة:

الحقلالوصف
tطابع زمني Unix (بالثواني) للحظة توقيع التسليم.
v1توقيع HMAC-SHA256 بترميز ست عشري للحمولة الموقّعة (الإصدار 1 من المخطط).

كيف يُحتسب التوقيع

  1. خذ الطابع الزمني t من الترويسة.

  2. خذ الحمولة الخام للطلب كما وصلت تمامًا (البايتات الخام — لا تعِد تسلسل JSON بعد تحليله).

  3. كوّن الحمولة الموقّعة بدمجهما بنقطة:

    signed_payload = "{t}" + "." + raw_body
  4. احسب HMAC-SHA256(signing_secret, signed_payload) ورمّزه ست عشريًا.

  5. يجب أن تساوي النتيجة قيمة الحقل v1.

تحقّق من الحمولة الخام

يغطي التوقيع البايتات التي أرسلتها TakeTheme بالضبط. فإذا حلّل إطار العمل لديك الـ JSON ثم أعدت تسلسله، فقد يختلف ترتيب المفاتيح أو المسافات ويفشل التحقق. اقرأ الحمولة الخام قبل تحليل JSON (مثل express.raw وrequest.get_data() وfile_get_contents('php://input')).

خطوات التحقق

للتحقق من ويب هوك بأمان:

  1. استخرج t وv1 من ترويسة X-TakeTheme-Signature.
  2. أعد حساب HMAC على "{t}.{raw_body}" بمفتاح التوقيع لديك.
  3. قارن قيمتك بـ v1 بمقارنة ثابتة الزمن لتجنّب هجمات التوقيت.
  4. (موصى به) ارفض الطلب إذا تجاوز فارق t عن الوقت الحالي نحو 5 دقائق، منعًا لإعادة تشغيل تسليمات مُلتقَطة.

أمثلة

import crypto from "node:crypto";

const TOLERANCE_SECONDS = 5 * 60;

/**
* @param {Buffer|string} rawBody Raw request body (unparsed).
* @param {string} header Value of the X-TakeTheme-Signature header.
* @param {string} secret The endpoint signing secret (whsec_...).
*/
export function verifySignature(rawBody, header, secret) {
if (!header) return false;

const parts = Object.fromEntries(
header.split(",").map((kv) => kv.split("=").map((s) => s.trim()))
);
const timestamp = parts.t;
const signature = parts.v1;
if (!timestamp || !signature) return false;

// Optional but recommended: reject stale deliveries.
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
if (age > TOLERANCE_SECONDS) return false;

const body = Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : rawBody;
const signedPayload = `${timestamp}.${body}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");

// Constant-time comparison.
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}

تدوير مفتاح التوقيع

إذا انكشف مفتاح توقيع في أي وقت، فدوّره. ويُصدر التدوير مفتاحًا جديدًا ويُبطل القديم فورًا، لذا انشر المفتاح الجديد على عنوانك سريعًا.

curl -X POST "https://api.taketheme.com/api/v1/store/webhooks/endpoints/{id}/rotate-secret" \
-H "tt-api-key: tt_xxx"
{ "secret": "whsec_9f2c...41ab" }

وكما في استجابة الإنشاء، يُعاد المفتاح الجديد مرة واحدة فقط — احفظه في مكان آمن.

تُعرض المفاتيح مرة واحدة

تحفظ TakeTheme مفاتيح التوقيع مشفّرة ولا يمكنها عرضها مجددًا بعد الإنشاء أو التدوير أبدًا. وإذا فقدت مفتاحًا، فدوّره للحصول على مفتاح جديد.

حل المشكلات

العَرَضالسبب المرجّح
التوقيع لا يطابق أبدًاتتحقق من JSON بعد تحليله وإعادة تسلسله. استخدم الحمولة الخام.
يعمل محليًا ويفشل في الإنتاجوسيط أو برمجية وسيطة تعيد كتابة الحمولة. تحقّق قبل أي تحليل للحمولة.
إخفاقات متقطعة بعد تغيير المفتاحالمفتاح القديم ما زال منشورًا في مكان ما. أكمل نشر المفتاح المدوَّر.
كل التسليمات تُرفض كـ"قديمة"انحراف ساعة الخادم. زامنها عبر NTP أو وسّع هامش الطابع الزمني لديك.