تخطَّ إلى المحتوى
ARAB CODEمجتمع المطورين العرب

بناء واجهات API آمنة بين الواجهة الأمامية والخادم

درسنُشر ٢٤ أكتوبر ٢٠٢٦ · 0 رد · 1 مشاهدة#api#أمان#nodejs#backend
قبل ساعتين

مقدمة

الواجهة الأمامية تعمل على جهاز المستخدم، ويمكنه تعديل أي شيء فيها: الطلبات، والقيم المخفية، وكود JavaScript نفسه. لذلك كل قرار أمني يُتخذ على الخادم. هذا الدرس يجمع أهم القواعد لبناء API لا تثق بالعميل.

أهداف التعلم

  • التحقق من كل مدخل على الخادم بمخطط واضح.
  • الفصل بين المصادقة (من أنت؟) والتفويض (ماذا يحق لك؟).
  • إعداد ملفات تعريف الارتباط للجلسات بأمان.
  • فهم CORS وCSRF وتحديد معدل الطلبات.

1. تحقق من المدخلات بمخطط

import { z } from 'zod';

const CreateTopic = z.object({
  title: z.string().trim().min(8).max(160),
  body: z.string().trim().min(20).max(40_000),
  tags: z.array(z.string().trim().min(1).max(30)).max(5).default([]),
}).strict(); // يرفض أي حقل غير متوقع مثل "authorId" أو "isPinned"

app.post('/api/topics', async (req, res) => {
  const parsed = CreateTopic.safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: 'بيانات غير صالحة' });
  // …
});

.strict() مهم: يمنع المستخدم من تمرير حقول لم تقصدها (هجوم «الإسناد الجماعي»).

2. المصادقة والتفويض

app.delete('/api/posts/:id', requireUser, async (req, res) => {
  const post = await db.post.findUnique({ where: { id: req.params.id } });
  if (!post) return res.status(404).end();
  const isOwner = post.authorId === req.user.id;
  if (!isOwner && !req.user.permissions.includes('posts.moderate')) {
    return res.status(404).end(); // لا تكشف وجود المورد لمن لا يملك الحق
  }
  // …
});
  • لا تأخذ هوية المستخدم من جسم الطلب أبداً (req.body.userId)؛ خذها من الجلسة.
  • تحقق من ملكية المورد في كل عملية؛ معرفة رقم المورد لا تعني الحق فيه (ثغرة IDOR).

3. الجلسات وملفات تعريف الارتباط

res.cookie('session', token, {
  httpOnly: true,     // لا يقرؤه JavaScript ← يقلل أثر XSS
  secure: true,       // HTTPS فقط
  sameSite: 'lax',    // يقلل CSRF
  path: '/',
  maxAge: 30 * 24 * 3600 * 1000,
});

خزّن في قاعدة البيانات بصمة رمز الجلسة (SHA-256) لا الرمز نفسه، حتى لا يُستغل تسريب القاعدة لانتحال الجلسات، وأبطل الجلسات عند تغيير كلمة المرور.

4. CSRF وCORS

  • CSRF: موقع خبيث يرسل طلباً إلى موقعك من متصفح مستخدم مسجل. مع SameSite=Lax والتحقق من ترويسة Origin في الطلبات المغيِّرة (POST/PUT/DELETE) تغلق معظم السيناريوهات.
  • CORS ليس آلية حماية للخادم بل قاعدة في المتصفح تحدد من يقرأ الردود. لا تستخدم Access-Control-Allow-Origin: * مع ملفات تعريف الارتباط. إن كانت الواجهة والخادم على النطاق نفسه فلا تحتاج CORS أصلاً.

5. تحديد معدل الطلبات

// عدّاد في قاعدة البيانات أو Redis حتى يعمل عبر كل نسخ الخادم
await limit(`login:${ip}`, 10, 15 * 60);       // 10 محاولات / 15 دقيقة لكل IP
await limit(`login-user:${username}`, 5, 15 * 60);

العداد في ذاكرة العملية لا يكفي إن كان لديك أكثر من نسخة للخادم.

6. رسائل الخطأ

أعد للعميل رسالة عامة ورمزاً قصيراً، وسجّل التفاصيل على الخادم مع معرّف للطلب. لا ترسل نص الاستثناء أو الاستعلام أو مسارات الملفات إلى المستخدم.

تمرين عملي

  1. اكتب نقطة API لتعديل ملف المستخدم، مع مخطط strict() يرفض الحقل role.
  2. اكتب اختباراً يحاول فيه المستخدم «أ» حذف مشاركة المستخدم «ب»، وتأكد أن الرد 404 وأن المشاركة باقية.
  3. أضف تحديد معدل لنقطة تسجيل الدخول واختبر الرد 429.

أخطاء شائعة

  • التحقق في الواجهة الأمامية فقط.
  • الاعتماد على إخفاء الأزرار بدل فحص الصلاحية على الخادم.
  • تخزين رموز الجلسات في localStorage (مكشوفة لأي XSS).
  • رسائل خطأ تكشف هل البريد مسجل أم لا في صفحات الدخول والاستعادة.

الخلاصة

تعامل مع كل طلب كأنه قادم من مهاجم: تحقق من شكله، وحدد صاحبه من الجلسة، وتحقق من حقه في المورد المحدد، وقيّد المعدل، وأعد أخطاء لا تكشف التفاصيل.

مصادر مقترحة

  • OWASP API Security Top 10.
  • OWASP Cheat Sheet: «Session Management» و«Cross-Site Request Forgery Prevention».
  • توثيق MDN عن CORS.

الرد على الموضوع

سجّل الدخول للمشاركة في النقاش.

نقاشات مشابهة