الاتجاه المعاكس للأمان
كل الدروس السابقة ركّزت على تأمين الـ API الذي تبنيه أنت. هذا الدرس يقلب الاتجاه: كيف تستهلك API يبنيه غيرك بأمان؟ هذه الفئة أُضيفت حديثًا كـ API10 ضمن OWASP API Security Top 10 2023، تحديدًا لأن معظم التطبيقات الحديثة تتكامل مع عشرات الخدمات الخارجية: بوابات دفع، خدمات تحقق هوية، أدوات تحليلات، ونماذج ذكاء اصطناعي.
💡 الفرضية الخاطئة الشائعة: "هذه بيانات من API رسمي موثوق، لا داعي للتحقق منها كما أتحقق من مدخلات المستخدم." هذه الفرضية بالذات هي الثغرة.
لماذا نثق بيانات الطرف الثالث أكثر مما يجب؟
مدخلات المستخدم المباشرة: 🔴 نتحقق منها دائمًا (تعلمناه في درس Input Validation)
استجابة API خارجي موثوق: 🟡 غالبًا نثق بها دون فحص كافٍ
لكن الطرف الثالث نفسه قد:
- يتعرّض للاختراق (سلسلة التوريد تشمل تبعياتك الخارجية أيضًا)
- يتغيّر سلوكه فجأة (تحديث API غير متوقع، حقل جديد، نوع بيانات مختلف)
- يُعيد توجيه طلبك (Redirect) لخادم مختلف تمامًا عن الذي تظنه
سيناريوهات هجوم واقعية
1. حقن عبر بيانات "مُخصَّبة" (Enrichment)
// خدمة خارجية تُثري بيانات المستخدم (مثل التحقق من صحة عنوان بريد أو شركة)
app.post('/api/users/enrich', async (req, res) => {
const response = await fetch(`https://enrichment-service.example.com/lookup?email=${req.body.email}`);
const { companyName, jobTitle } = await response.json();
// خطر: تخزين مباشر بلا تعقيم أو تحقق
await db.$executeRawUnsafe(
`UPDATE users SET company = '${companyName}' WHERE id = ${req.user.id}`
);
res.json({ companyName, jobTitle });
});
لو تعرّضت خدمة الإثراء الخارجية لاختراق (أو سمحت لأي مستخدم بتسجيل اسم
شركة بصياغة SQL خبيثة)، فإن companyName يحمل الآن Payload حقن SQL،
ينفَّذ مباشرة ضد قاعدة بياناتك بمجرد استخدام $executeRawUnsafe.
الإصلاح: عامل companyName كمدخل مستخدم غير موثوق تمامًا — استعلام معامل (parameterized) وتحقق من الشكل المتوقع:
const enrichmentSchema = z.object({
companyName: z.string().max(200).regex(/^[\p{L}\p{N}\s.,&-]+$/u),
jobTitle: z.string().max(200),
});
const { companyName, jobTitle } = enrichmentSchema.parse(await response.json());
await db.user.update({ where: { id: req.user.id }, data: { company: companyName } });
2. تحويلات (Redirects) خبيثة
// سيئ — يتبع أي تحويل يرسله الطرف الثالث تلقائيًا ويرسل بيانات حساسة معه
async function verifyPayment(paymentServiceUrl, cardToken) {
const response = await fetch(paymentServiceUrl, {
method: 'POST',
body: JSON.stringify({ cardToken }), // بيانات حساسة بجسم الطلب
});
return response.json();
}
لو تعرّضت خدمة الدفع لاختراق جزئي، يمكن أن ترد بتحويل HTTP (301/307/308)
نحو خادم يتحكم به المهاجم. مكتبات fetch الافتراضية تتبع هذا التحويل
تلقائيًا وتعيد إرسال جسم الطلب — بما فيه cardToken — لخادم المهاجم.
الإصلاح: امنع تتبع التحويلات تلقائيًا، وتحقق يدويًا من أن أي تحويل يشير لوجهة معروفة:
async function verifyPayment(paymentServiceUrl, cardToken) {
const TRUSTED_REDIRECT_HOSTS = ['payments.trusted-provider.com'];
const response = await fetch(paymentServiceUrl, {
method: 'POST',
body: JSON.stringify({ cardToken }),
redirect: 'manual',
});
if (response.status >= 300 && response.status < 400) {
const target = new URL(response.headers.get('location'));
if (!TRUSTED_REDIRECT_HOSTS.includes(target.hostname)) {
throw new Error('تحويل غير موثوق من مزوّد الدفع — تم الرفض');
}
// أعد الطلب صراحة للوجهة المتحقق منها فقط، لا لأي وجهة أخرى
}
return response.json();
}
3. حقن عبر أسماء الكيانات
سيناريو من العالم الحقيقي: تطبيق يتكامل مع خدمة استضافة كود خارجية
ويعرض "اسم المستودع" في تقارير داخلية. مهاجم ينشئ مستودعًا باسم يحوي
صياغة SQL أو HTML خبيثة. عند سحب قائمة المستودعات عبر API الخدمة
الخارجية وعرضها أو تخزينها دون تعقيم، يُنفَّذ المحتوى الخبيث محليًا —
سواء كحقن قاعدة بيانات أو كـ XSS عند العرض بالمتصفح.
هذا يذكّرنا أن أي حقل نصي حر يملأه مستخدم لدى طرف ثالث (اسم، وصف، تعليق) يصلك أنت كبيانات "خارجية" تحتاج نفس معاملة أي مدخل مستخدم مباشر.
قائمة تحقق للتكامل الآمن
قبل الربط مع أي API خارجي:
✅ قيّم وضعه الأمني (شهادات، سجل اختراقات سابقة، توثيق أمني)
✅ افرض TLS لكل اتصال — لا استثناءات
عند استهلاك الاستجابة:
✅ تحقق من الشكل (Schema Validation) قبل أي استخدام أو تخزين
✅ عامل كل حقل نصي كمدخل غير موثوق (لا فرق عن مدخل مستخدم مباشر)
✅ لا تتبع Redirects تلقائيًا — تحقق من الوجهة أولًا
✅ لا تنفّذ استعلامات ديناميكية ببيانات خارجية (parameterized queries دائمًا)
✅ حدّد Timeout قصير — تبعية بطيئة لا يجب أن تُبطئ نظامك كاملًا
المراقبة المستمرة:
✅ راقب تغيّر سلوك الـ API الخارجي (حقول جديدة، أنواع بيانات مختلفة)
✅ خطة بديلة (Fallback) لو توقفت الخدمة الخارجية أو تصرّفت بشكل غريب
العلاقة مع SSRF
هذه الفئة وثيقة الصلة بدرس SSRF السابق: أي ميزة "تجلب أو تتبع رابطًا من طرف خارجي" — كالتحويلات في المثال الثاني أعلاه — معرّضة للفئتين معًا. الفرق: SSRF يركّز على الوجهة التي تطلبها أنت بناءً على مدخل مستخدم، بينما Unsafe Consumption يركّز على البيانات التي ترجع إليك من تكامل تثق به أصلًا.
⚠️ القاعدة الذهبية: الثقة بمصدر البيانات (رسمي، معروف، موثّق) لا تعني الثقة بمحتوى البيانات نفسها. افحص المحتوى دائمًا، بغض النظر عن مصدره.
🎯 التالي: مشروع ختامي: تأمين REST API