مصادقة Webhook - أمّن نقاط النهاية الخاصة بك
يوفّر finlight طرق مصادقة متعدّدة لتأمين عمليات تسليم webhook الخاصة بك. يتضمّن كل webhook التحقّق من التوقيع افتراضيًا، مع طبقات مصادقة إضافية اختيارية لتعزيز الأمان.
يدعم finlight أربع طرق مصادقة يمكن استخدامها بشكل فردي أو مجتمعة:
بلا
لا توجد مصادقة إضافية بخلاف التحقّق الافتراضي من التوقيع.
متى تُستخدم:
- بيئات الاختبار والتطوير
- نقاط النهاية الداخلية خلف شبكات آمنة
- عندما يوفّر التحقّق من التوقيع أمانًا كافيًا
ملاحظة: يظلّ التحقّق من التوقيع مُضمَّنًا في كل طلب webhook بغضّ النظر عن هذا الإعداد.
ترويسة X-Finlight-Key
ترسل مفتاح API الخاص بك في ترويسة مخصّصة X-Finlight-Key مع كل طلب webhook.
التهيئة:
- قدّم مفتاح API الخاص بك أثناء إعداد webhook
- سيُضمَّن المفتاح في ترويسة
X-Finlight-Key
التنفيذ: ينبغي أن تتحقّق نقطة النهاية الخاصة بك من الترويسة الواردة:
const finlightKey = req.headers['x-finlight-key']
if (finlightKey !== 'your-expected-api-key') {
return res.status(401).send('Invalid API key')
}
الترويسات المُرسَلة:
X-Finlight-Key: your-api-key-value
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
المصادقة الأساسية
مصادقة HTTP الأساسية ببيانات اعتماد اسم المستخدم/كلمة المرور.
التهيئة:
- عيّن اسم المستخدم وكلمة المرور أثناء إعداد webhook
- تُرمَّز بيانات الاعتماد بترميز base64 وتُرسَل في ترويسة
Authorization
التنفيذ: تتلقّى نقطة النهاية الخاصة بك مصادقة HTTP الأساسية القياسية:
const auth = req.headers.authorization
if (!auth || !auth.startsWith('Basic ')) {
return res.status(401).send('Missing Basic Auth')
}
const credentials = Buffer.from(auth.slice(6), 'base64').toString()
const [username, password] = credentials.split(':')
if (username !== 'expected-user' || password !== 'expected-pass') {
return res.status(401).send('Invalid credentials')
}
الترويسات المُرسَلة:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
التحقّق من التوقيع
أمان تلقائي: يتضمّن كل طلب webhook التحقّق من التوقيع بغضّ النظر عن طريقة المصادقة التي تختارها.
كيف يعمل:
- يُنشئ finlight طابعًا زمنيًا عند إرسال webhook
- ينشئ رسالة عبر الدمج:
timestamp + '.' + payload - يوقّع الرسالة باستخدام HMAC-SHA256 بمفتاح webhook السري الخاص بك
- يرسل كلًّا من التوقيع والطابع الزمني في الترويسات
الترويسات المُضمَّنة:
X-Webhook-Signature: sha256=computed-signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
خوارزمية التوقيع:
message = timestamp + '.' + raw_request_body
signature = HMAC-SHA256(message, webhook_secret)
عندما لا تكون ترويسة X-Webhook-Timestamp موجودة، تكون الرسالة هي المتن الخام وحده. قارن النتيجة في زمن ثابت، وارفض عمليات التسليم التي يتجاوز عمر طابعها الزمني خمس دقائق.
يجب أن يكون المتن هو بايتات الطلب الخام غير المُحلّلة. أي برمجية وسيطة تفكّ ترميز JSON وتعيد ترميزه قبل أن تقرأه تغيّر تسلسل البايتات وتُبطل التوقيع — وهذا هو السبب الأكثر شيوعًا لفشل التحقّق. اقرأ المتن كسلسلة نصية خام أو كمخزن مؤقّت، وتحقّق منه، وعندئذٍ فقط حلّله.
التحقّق باستخدام عميل رسمي
تتضمّن كل مكتبة عملاء دالة مساعدة تُجري عملية التحقّق كاملةً نيابةً عنك: فهي تقبل البادئة sha256= في التوقيع، وتقارن في زمن ثابت، وتفرض نافذة إعادة تشغيل مدّتها خمس دقائق، وتعيد المقال بعد تحليله. وهي تطلق استثناءً أو تعيد خطأً عند فشل التحقّق — ردّ على ذلك دائمًا برمز 4xx ولا تعالج الحمولة إطلاقًا.
التحقّق من webhook
import express from 'express'
import { WebhookService } from 'finlight-client'
const app = express()
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const article = WebhookService.constructEvent(
req.body.toString(),
req.headers['x-webhook-signature'] as string,
process.env.WEBHOOK_SECRET!,
req.headers['x-webhook-timestamp'] as string,
)
console.log('New article:', article.title)
res.sendStatus(200)
} catch (err) {
console.error('Webhook verification failed:', err)
res.sendStatus(400)
}
})
أفضل ممارسات الأمان
التحقّق من الطابع الزمني
تفرض دوال constructEvent المساعدة أعلاه نافذة إعادة تشغيل مدّتها خمس دقائق أصلًا، لذا لا تحتاج إلى هذا إلا إذا كنت تتحقّق من التواقيع يدويًا:
function isTimestampValid(timestamp, toleranceSeconds = 300) {
const now = Date.now()
const requestTime = new Date(timestamp).getTime()
const difference = Math.abs(now - requestTime) / 1000
return difference <= toleranceSeconds
}
التخزين الآمن لبيانات الاعتماد
- متغيّرات البيئة: خزّن جميع الأسرار في متغيّرات البيئة
- إدارة الأسرار: استخدم AWS Secrets Manager أو HashiCorp Vault أو ما شابه
- لا تكتبها ضمن الكود مطلقًا: لا تُودِع الأسرار في نظام التحكّم بالإصدارات أبدًا
- التدوير المنتظم: حدِّث أسرار webhook دوريًا
للحصول على إرشادات إعداد webhook، راجع وثائق webhook الرئيسية. وللاختبار الشامل، راجع دليل اختبار webhook.