تخطّى إلى المحتوى الرئيسي
‏Server Actions نقاط نهاية عامة: كيف نؤمّن عمليات الكتابة في Next.js

‏Server Actions نقاط نهاية عامة: كيف نؤمّن عمليات الكتابة في Next.js

July 13, 2026
هندسة الواجهات
6 دقيقة قراءة

كل Server Action في Next.js يتحول عند البناء إلى نقطة نهاية HTTP عامة. توجيه "use server" يحدد حدوداً شبكية وليس دالة خاصة: أي عميل يستطيع استدعاء الـ Action عبر طلب POST مُصاغ يدوياً، سواء عرضت الواجهة الزر الذي يستدعيه أم لا. في Devya نتعامل مع كل Action كما نتعامل مع أي Route Handler — نتحقق من المدخلات وقت التشغيل، ونفحص الصلاحيات داخل الـ Action نفسه، ونراجع ما تلتقطه الـ Closures. هذه هي الممارسات التي نطبقها في مشاريع Next.js لدينا.

أهم النقاط

• كل Server Action يتحول إلى نقطة نهاية POST عامة معرّفة بمعرّف يُولَّد وقت البناء. إخفاء الزر الذي يستدعيها أو تعطيله ليس تحكماً في الوصول.

• أنواع TypeScript على معاملات الدالة تُحذف وقت البناء. نتحقق من مدخلات كل Action وقت التشغيل بمكتبة مخططات مثل Zod ونعرّف المعاملات بنوع unknown حتى يجبرنا المصرّف على التحقق.

• المصادقة والتفويض مكانهما داخل كل Action أو في غلاف مشترك، لا في الـ Middleware وحده. الثغرة CVE-2025-29927 (مارس 2025) أثبتت أن الاعتماد على الـ Middleware وحده قابل للتجاوز في نشر Next.js الذاتي.

• المتغيرات التي تلتقطها Closure الخاصة بـ Action مضمّن تُشفَّر وتُرسل إلى العميل ثم تعود عند الاستدعاء. اضبطوا متغير البيئة NEXT_SERVER_ACTIONS_ENCRYPTION_KEY عند تشغيل أكثر من خادم.

• إعدادا serverActions.allowedOrigins و serverActions.bodySizeLimit في next.config يقويان طبقة النقل. كما يرفض Next.js افتراضياً طلبات الـ Actions عابرة الأصل بمقارنة ترويستي Origin و Host.

إلى ماذا يتحول Server Action فعلياً؟

الـ Server Action نقطة نهاية HTTP عامة. عندما تُعلَّم دالة بتوجيه "use server" يمنحها نظام البناء في Next.js معرّف Action ويربطها بمعالج POST الخاص بالإطار. استدعاء الـ Action يرسل طلب POST إلى عنوان الصفحة الحالية مع ترويسة Next-Action تحمل ذلك المعرّف، والمعاملات مُسلسلة في جسم الطلب. لا يوجد عنوان منفصل يمكن عزله خلف جدار حماية — الـ Action يمر عبر نفس مسار الصفحة.

يترتب على ذلك أمران. أولاً، كل دالة مُصدَّرة من ملف "use server" قابلة للوصول عبر الشبكة؛ الإصدارات الحديثة من Next.js تحذف الـ Actions غير المستوردة، لكن النموذج الذهني الآمن أن كل Action مُصدَّر نقطة نهاية حية. ثانياً، المستدعي يتحكم في الحمولة بالكامل. الإطار يفك تسلسل ما يصل أياً كان — لا شيء يضمن مطابقة المعاملات لتوقيع TypeScript.

لماذا لا يكفي إخفاء الزر؟

العرض الشرطي ليس تفويضاً. إذا كانت لوحة الإدارة تعرض زر الحذف للمشرفين فقط، فالفحص يعيش في الواجهة — بينما معرّف الـ Action موجود في حزمة العميل، وأي مستخدم يستطيع إعادة إرسال الطلب بحمولته الخاصة. هذا نفس درس واجهات REST الكلاسيكية: لا أحد يحمي DELETE /api/users/:id بإخفاء رابط. الـ Server Action يستحق نفس الحذر الذي نمنحه لأي Route Handler مكشوف.

كيف نتحقق من مدخلات الـ Server Action؟

نفترض أن كل معامل خاضع لسيطرة المهاجم، لأنه كذلك فعلاً. أنواع TypeScript على التوقيع لا تتحقق من شيء وقت التشغيل. نمطنا: نعرّف المدخل بنوع unknown ونحلله بـ Zod في أول سطر من الـ Action — مثلاً مخطط يشترط أن يكون userId بصيغة UUID وأن يكون role إحدى قيمتي viewer أو editor، ثم استدعاء UpdateRole.parse(input) قبل أي وصول للبيانات. بهذا يرفض المصرّف تمرير بيانات غير مُتحقق منها.

في نماذج الإدخال يكون المعامل كائن FormData — نحوّله بـ Object.fromEntries ونمرره عبر نفس المخطط. لاحظوا ما يستبعده المخطط: إذا لم تكن قيمة admin ضمن قائمة role المسموحة، يموت هجوم الإسناد الجماعي عند خطوة التحقق بدل أن يصل إلى قاعدة البيانات.

أين توضع فحوص الصلاحيات؟

داخل الـ Action، بجوار البيانات التي يلمسها. الـ Middleware يعمل على طلبات الـ Actions أيضاً، لكنه يجيب عن سؤال: هل يحق لهذا الطلب الوصول إلى هذا المسار؟ لا عن سؤال: هل يحق لهذا المستخدم تعديل هذا الصف؟ الثغرة CVE-2025-29927 في مارس 2025 — تجاوز عبر انتحال ترويسة في نشر Next.js الذاتي — وضّحت النقطة الأهم: حين يكون الـ Middleware طبقة الحماية الوحيدة، تكفي ثغرة واحدة في الإطار لإسقاطها كلها.

نغلّف الـ Actions بدالة مشتركة authedAction تحمّل الجلسة وترفض الاستدعاءات غير الموثقة وتمرر المستخدم إلى جسم الـ Action. وداخل الـ Action يرشّح استعلام قاعدة البيانات بالملكية — مثلاً البحث عن المشروع بمعرّفه ومعرّف مالكه ownerId معاً قبل حذفه. الغلاف يثبت هوية المستدعي؛ ومرشّح الملكية يثبت أنه يملك ما يعدّله. تجاهل الخطوة الثانية هو ثغرة IDOR بعينها.

ماذا تلتقط الـ Closures المضمّنة ومن يستطيع قراءتها؟

الـ Action المعرّف داخل Server Component يلتقط متغيرات من نطاق العرض. يشفّر Next.js هذه القيم بمفتاح يُولَّد وقت البناء، ويضمّن النص المشفّر في حمولة الصفحة، ثم يفك تشفيره عند الاستدعاء. ملاحظتان تشغيليتان من نشراتنا: مع النشر التدريجي أو تعدد الخوادم، يولّد كل بناء مفتاحاً مختلفاً وقد يفشل فك التشفير بين الإصدارات — اضبطوا متغير البيئة NEXT_SERVER_ACTIONS_ENCRYPTION_KEY لتوحيده. والتشفير ليس رخصة لالتقاط الأسرار: النص المشفّر يعيش لدى العميل، لذلك نمرر المعرّفات ونعيد جلب البيانات الحساسة من الخادم.

أي إعدادات تقوّي حماية الـ Server Actions؟

• الاستدعاء عابر الأصل: يقارن Next.js ترويستي Origin و Host ويرفض عدم التطابق؛ وخلف الوكلاء أو مع تعدد النطاقات يجب ضبط serverActions.allowedOrigins صراحة.

• الحمولات الضخمة: يحدّ serverActions.bodySizeLimit جسم الطلب بـ 1 ميغابايت افتراضياً — نرفعه عند الحاجة فقط لا بشكل عام.

• تسرب الأسرار إلى العميل: واجهات React التجريبية للتلويث experimental_taintObjectReference و experimental_taintUniqueValue ترمي خطأ عند تمرير كائنات ملوّثة إلى مكونات العميل.

• الإساءة والإغراق: نحدّ المعدل داخل الغلاف المشترك للـ Actions، بمفتاح هو معرّف المستخدم أو عنوان IP.

• المعرّفات القديمة: تتغير معرّفات الـ Actions مع كل بناء، فقد يفشل عميل على نسخة قديمة من الصفحة بعد النشر — عالجوا فشل الـ Action بحدود خطأ ورسالة تدعو للتحديث.

الأسئلة الشائعة

س: هل تملك الـ Server Actions حماية مدمجة من CSRF؟

ج: جزئياً. لا تقبل الـ Actions إلا طلبات POST، ويرفض Next.js الطلبات التي لا تطابق ترويسة Origin فيها ترويسة Host. خلف وكيل أو مع نطاقات متعددة يجب إدراج الأصول المسموحة في serverActions.allowedOrigins صراحة.

س: هل يمكن استدعاء Server Action من صفحة لا يراها المستخدم؟

ج: نعم. نقاط نهاية الـ Actions لا تعرف ما عرضته الواجهة. أي عميل يعرف معرّف الـ Action يستطيع إرسال الطلب، لذلك يجب على كل Action أن يوثّق ويفوّض بنفسه.

س: هل يتحقق TypeScript من معاملات الـ Action وقت التشغيل؟

ج: لا. الأنواع تُحذف عند الترجمة. يستطيع المستدعي إرسال أي حمولة قابلة للتسلسل، ولهذا يعد التحقق وقت التشغيل بـ Zod أو Valibot أو ArkType أمراً غير قابل للتفاوض.

س: هل يعمل الـ Middleware على طلبات الـ Server Actions؟

ج: نعم، تمر طلبات الـ Actions عبر الـ Middleware كأي طلب آخر. لكن الاعتماد عليه وحده حماية على مستوى المسار فقط، وقد ثبت تجاوزه عبر CVE-2025-29927، وهو أعمى عن ملكية الصفوف. اجعلوه طبقة واحدة لا الطبقة الوحيدة.

س: هل من الآمن وضع دوال مساعدة في ملف معلَّم بـ "use server"؟

ج: لا. كل تصدير من ملف "use server" يصبح نقطة نهاية قابلة للوصول. ضعوا الدوال المساعدة في ملف منفصل غير معلَّم وصدّروا الـ Actions الحقيقية فقط من ملفات الـ Actions.