مكونات خادم React (RSC) في App Router هي نموذج جديد يلغي الكثير من التكرار والمخاطر المحتملة المرتبطة بالطرق التقليدية. نظرًا لحداثة هذا النموذج، قد يواجه المطورون وفرق الأمان صعوبة في مواءمة بروتوكولات الأمان الحالية مع هذا النموذج.
يهدف هذا المستند إلى تسليط الضوء على بعض المجالات التي يجب الانتباه إليها، وما هي إجراءات الحماية المضمنة، ويتضمن دليلًا لمراجعة التطبيقات. نركز بشكل خاص على مخاطر الكشف غير المقصود عن البيانات.
اختيار نموذج معالجة البيانات الخاص بك
مكونات خادم React تزيل الحدود الفاصلة بين الخادم والعميل. تعد معالجة البيانات أمرًا بالغ الأهمية لفهم مكان معالجة المعلومات وإتاحتها لاحقًا.
أول شيء نحتاج إلى فعله هو اختيار نهج معالجة البيانات المناسب لمشروعنا.
- واجهات برمجة تطبيقات HTTP (موصى بها للمشاريع الكبيرة الحالية / المنظمات)
- طبقة الوصول إلى البيانات (موصى بها للمشاريع الجديدة)
- الوصول إلى البيانات على مستوى المكون (موصى بها للنماذج الأولية والتعلم)
نوصي بالالتزام بنهج واحد وعدم المزج بين النهج كثيرًا. هذا يجعل الأمور واضحة لكل من المطورين الذين يعملون على قاعدة الكود الخاصة بك ومدققي الأمان فيما يتوقعونه. تبرز الاستثناءات كمشبوهة.
واجهات برمجة تطبيقات HTTP
إذا كنت تتبنى مكونات الخادم في مشروع موجود، فإن النهج الموصى به هو معالجة مكونات الخادم في وقت التشغيل على أنها غير آمنة / غير موثوقة افتراضيًا مثل SSR أو داخل العميل. لذلك لا يوجد افتراض لشبكة داخلية أو مناطق ثقة ويمكن للمهندسين تطبيق مفهوم الثقة الصفرية. بدلاً من ذلك، تقوم فقط باستدعاء نقاط نهاية API مخصصة مثل REST أو GraphQL باستخدام fetch()
من مكونات الخادم تمامًا كما لو كان يتم تنفيذها على العميل. مع تمرير أي ملفات تعريف الارتباط.
إذا كان لديك getStaticProps
/getServerSideProps
موجود يتصل بقاعدة بيانات، فقد ترغب في توحيد النموذج ونقل هذه إلى نقاط نهاية API أيضًا بحيث يكون لديك طريقة واحدة للقيام بالأشياء.
احذر من أي تحكم في الوصول يفترض أن طلبات الجلب من الشبكة الداخلية آمنة.
يتيح لك هذا النهج الحفاظ على الهياكل التنظيمية الحالية حيث يمكن لفرق الخلفية الحالية المتخصصة في الأمان تطبيق ممارسات الأمان الحالية. إذا كانت هذه الفرق تستخدم لغات أخرى غير JavaScript، فإن ذلك يعمل جيدًا في هذا النهج.
لا يزال يستفيد من العديد من مزايا مكونات الخادم عن طريق إرسال كود أقل إلى العميل ويمكن أن تنفذ شلالات البيانات المتأصلة بزمن انتقال منخفض.
طبقة الوصول إلى البيانات
نهجنا الموصى به للمشاريع الجديدة هو إنشاء طبقة وصول إلى بيانات منفصلة داخل قاعدة كود JavaScript الخاصة بك وتوحيد جميع عمليات الوصول إلى البيانات هناك. يضمن هذا النهج وصولًا متسقًا إلى البيانات ويقلل من فرص حدوث أخطاء في التفويض. كما أنه أسهل في الصيانة نظرًا لأنك تقوم بتوحيد كل شيء في مكتبة واحدة. ربما توفير تماسك أفضل للفريق مع لغة برمجة واحدة. يمكنك أيضًا الاستفادة من أداء أفضل مع حمل تشغيل أقل، والقدرة على مشاركة ذاكرة تخزين مؤقت عبر أجزاء مختلفة من الطلب.
تقوم ببناء مكتبة JavaScript داخلية توفر عمليات التحقق من الوصول إلى البيانات المخصصة قبل إعطائها للمستدعي. مشابه لنقاط نهاية HTTP ولكن في نموذج الذاكرة نفسه. يجب أن يقبل كل API المستخدم الحالي ويتحقق مما إذا كان المستخدم يمكنه رؤية هذه البيانات قبل إرجاعها. المبدأ هو أن جسم دالة مكون الخادم يجب أن يرى فقط البيانات التي يُصرح للمستخدم الحالي الذي يصدر الطلب بالوصول إليها.
من هذه النقطة، تتولى ممارسات الأمان العادية لتنفيذ واجهات برمجة التطبيقات.
import { cache } from 'react';
import { cookies } from 'next/headers';
// تجعل الطرق المساعدة المخزنة من السهل الحصول على نفس القيمة في العديد من الأماكن
// دون تمريرها يدويًا. هذا يثبط من تمريرها من مكون
// الخادم إلى مكون الخادم مما يقلل من خطر تمريرها إلى مكون
// العميل.
export const getCurrentUser = cache(async () => {
const token = cookies().get('AUTH_TOKEN');
const decodedToken = await decryptAndValidate(token);
// لا تضمّن الرموز السرية أو المعلومات الخاصة كحقول عامة.
// استخدم الفئات لتجنب تمرير الكائن بأكمله عن طريق الخطأ إلى العميل.
return new User(decodedToken.id);
});
import 'server-only';
import { getCurrentUser } from './auth';
function canSeeUsername(viewer: User) {
// معلومات عامة في الوقت الحالي، ولكن يمكن أن تتغير
return true;
}
function canSeePhoneNumber(viewer: User, team: string) {
// قواعد الخصوصية
return viewer.isAdmin || team === viewer.team;
}
export async function getProfileDTO(slug: string) {
// لا تمرر القيم، اقرأ القيم المخزنة، كما يحل مشكلة السياق ويجعلها كسولة بشكل أسهل
// استخدم واجهة برمجة تطبيقات قاعدة بيانات تدعم القوالب الآمنة للاستعلامات
const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`;
const userData = rows[0];
const currentUser = await getCurrentUser();
// أعد فقط البيانات ذات الصلة بهذا الاستعلام وليس كل شيء
// <https://www.w3.org/2001/tag/doc/APIMinimization>
return {
username: canSeeUsername(currentUser) ? userData.username : null,
phonenumber: canSeePhoneNumber(currentUser, userData.team)
? userData.phonenumber
: null,
};
}
يجب أن تعرض هذه الطرق كائنات آمنة ليتم نقلها إلى العميل كما هي. نحب أن نسمي هذه كائنات نقل البيانات (DTO) لتوضيح أنها جاهزة للاستهلاك من قبل العميل.
قد يتم استهلاكها فقط بواسطة مكونات الخادم في الممارسة العملية. هذا يخلق طبقة حيث يمكن لعمليات تدقيق الأمان التركيز بشكل أساسي على طبقة الوصول إلى البيانات بينما يمكن لواجهة المستخدم التكرار بسرعة. مساحة سطح أصغر وكمية أقل من الكود لتغطيتها يجعل من السهل اكتشاف مشكلات الأمان.
import {getProfile} from '../../data/user'
export async function Page({ params: { slug } }) {
// يمكن لهذه الصفحة الآن تمرير هذا الملف الشخصي بأمان مع العلم
// أنه لا ينبغي أن يحتوي على أي شيء حساس.
const profile = await getProfile(slug);
...
}
يمكن تخزين المفاتيح السرية في متغيرات البيئة ولكن يجب أن تصل طبقة الوصول إلى البيانات فقط إلى process.env
في هذا النهج.
الوصول إلى البيانات على مستوى المكون
نهج آخر هو وضع استعلامات قاعدة البيانات مباشرة في مكونات الخادم الخاصة بك. هذا النهج مناسب فقط للتكرار السريع وإنشاء النماذج الأولية. على سبيل المثال، لمنتج صغير مع فريق صغير حيث يكون الجميع على دراية بالمخاطر وكيفية مراقبتها.
في هذا النهج، سترغب في مراجعة ملفات "use client"
بعناية. أثناء التدقيق ومراجعة طلبات السحب، انظر إلى جميع الدوال المصدرة وإذا كانت توقيعات النوع تقبل كائنات واسعة جدًا مثل User
، أو تحتوي على خصائص مثل token
أو creditCard
. حتى الحقول الحساسة للخصوصية مثل phoneNumber
تحتاج إلى فحص إضافي. لا ينبغي أن يقبل مكون العميل بيانات أكثر من الحد الأدنى من البيانات التي يحتاجها لأداء وظيفته.
import Profile from './components/profile.tsx';
export async function Page({ params: { slug } }) {
const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`;
const userData = rows[0];
// مكشوف: يعرض هذا جميع الحقول في userData للعميل لأننا
// نمرر البيانات من مكون الخادم إلى العميل.
// هذا مشابه لإرجاع `userData` في `getServerSideProps`
return <Profile user={userData} />;
}
'use client';
// سيء: هذه واجهة خصائص سيئة لأنها تقبل بيانات أكثر بكثير مما يحتاجه
// مكون العميل وتشجع مكونات الخادم على تمرير كل تلك
// البيانات للأسفل. الحل الأفضل هو قبول كائن محدود يحتوي فقط
// على الحقول الضرورية لعرض الملف الشخصي.
export default async function Profile({ user }: { user: User }) {
return (
<div>
<h1>{user.name}</h1>
...
</div>
);
}
استخدم دائمًا استعلامات معلمة، أو مكتبة قاعدة بيانات تقوم بذلك نيابة عنك، لتجنب هجمات حقن SQL.
الخادم فقط
يمكن تمييز الكود الذي يجب أن ينفذ فقط على الخادم بـ:
import 'server-only';
سيؤدي هذا إلى حدوث خطأ في البناء إذا حاول مكون عميل استيراد هذه الوحدة. يمكن استخدام هذا لضمان عدم تسريب الكود الحساس أو منطق الأعمال الداخلي عن طريق الخطأ إلى العميل.
الطريقة الأساسية لنقل البيانات هي استخدام بروتوكول مكونات خادم React الذي يحدث تلقائيًا عند تمرير الخصائص إلى مكونات العميل. يدعم هذا التسلسل مجموعة شاملة من JSON. نقل الفئات المخصصة غير مدعوم وسينتج عنه خطأ.
لذلك، حيلة لطيفة لتجنب تعريض كائنات كبيرة جدًا للعميل عن طريق الخطأ هي استخدام class
لسجلات الوصول إلى البيانات الخاصة بك.
في إصدار Next.js 14 القادم، يمكنك أيضًا تجربة واجهات برمجة تطبيقات React Taint التجريبية عن طريق تمكين علم taint
في next.config.js
.
module.exports = {
experimental: {
taint: true,
},
};
يتيح لك هذا وضع علامة على كائن لا يجب السماح بتمريره إلى العميل كما هو.
import { experimental_taintObjectReference } from 'react';
export async function getUserData(id) {
const data = ...;
experimental_taintObjectReference(
'لا تمرر بيانات المستخدم إلى العميل',
data
);
return data;
}
import { getUserData } from './data';
export async function Page({ searchParams }) {
const userData = getUserData(searchParams.id);
return <ClientComponent user={userData} />; // خطأ
}
هذا لا يحمي ضد استخراج حقول البيانات من هذا الكائن وتمريرها:
export async function Page({ searchParams }) {
const { name, phone } = getUserData(searchParams.id);
// تعريض بيانات شخصية عمدًا
return <ClientComponent name={name} phoneNumber={phone} />;
}
للنصوص الفريدة مثل الرموز، يمكن أيضًا حظر القيمة الأولية باستخدام taintUniqueValue
.
import { experimental_taintObjectReference, experimental_taintUniqueValue } from 'react';
export async function getUserData(id) {
const data = ...;
experimental_taintObjectReference(
'لا تمرر بيانات المستخدم إلى العميل',
data
);
experimental_taintUniqueValue(
'لا تمرر الرموز إلى العميل',
data,
data.token
);
return data;
}
ومع ذلك، حتى هذا لا يحظر القيم المشتقة.
من الأفضل تجنب وصول البيانات إلى مكونات الخادم في المقام الأول - باستخدام طبقة الوصول إلى البيانات. يوفر فحص التلوث طبقة إضافية من الحماية ضد الأخطاء عن طريق تحديد القيمة، يرجى ملاحظة أن الدوال والفئات محظورة بالفعل من تمريرها إلى مكونات العميل. المزيد من الطبقات يقلل من خطر تسرب شيء ما.
بشكل افتراضي، تكون متغيرات البيئة متاحة فقط على الخادم. حسب الاصطلاح، يعرض Next.js أيضًا أي متغير بيئة مسبوق بـ NEXT_PUBLIC_
إلى العميل. يتيح لك هذا كشف بعض التكوينات الصريحة التي يجب أن تكون متاحة للعميل.
SSR مقابل RSC
للحمل الأولي، سيقوم Next.js بتشغيل كل من مكونات الخادم ومكونات العميل على الخادم لإنتاج HTML.
تعمل مكونات الخادم (RSC) في نظام وحدة نمطية منفصل عن مكونات العميل لتجنب الكشف عن المعلومات بين النظامين عن طريق الخطأ.
يجب اعتبار مكونات العميل التي يتم عرضها من خلال عرض جانب الخادم (SSR) على أنها نفس سياسة الأمان مثل عميل المتصفح. لا ينبغي أن تحصل على أي بيانات مميزة أو واجهات برمجة تطبيقات خاصة. من غير المستحسن استخدام الاختراقات لمحاولة تجنب هذه الحماية (مثل تخزين البيانات على الكائن العام). المبدأ هو أن هذا الكود يجب أن يكون قادرًا على التنفيذ بنفس الطريقة على الخادم كما هو الحال على العميل. تماشيًا مع ممارسات الأمان الافتراضية، سيفشل Next.js في البناء إذا تم استيراد وحدات server-only
من مكون عميل.
قراءة
في Next.js App Router، يتم تنفيذ قراءة البيانات من قاعدة بيانات أو API عن طريق عرض صفحات مكونات الخادم.
مدخلات الصفحات هي searchParams في URL، ومعلمات ديناميكية معينة من URL والرؤوس. يمكن إساءة استخدام هذه لتكون قيمًا مختلفة من قبل العميل. لا ينبغي الوثوق بها ويجب إعادة التحقق منها في كل مرة يتم قراءتها. على سبيل المثال، لا ينبغي استخدام searchParam لتتبع أشياء مثل ?isAdmin=true
. فقط لأن المستخدم موجود في /[team]/
لا يعني أنه لديه حق الوصول إلى هذا الفريق، وهذا يحتاج إلى التحقق عند قراءة البيانات. المبدأ هو إعادة قراءة تحكم الوصول و cookies()
دائمًا عند قراءة البيانات. لا تمررها كخصائص أو معلمات.
لا ينبغي أبدًا أن يؤدي عرض مكون خادم إلى تأثيرات جانبية مثل الطفرات. هذا ليس فريدًا لمكونات الخادم. يثبط React بشكل طبيعي التأثيرات الجانبية حتى عند عرض مكونات العميل (خارج useEffect)، عن طريق القيام بأشياء مثل العرض المزدوج.
بالإضافة إلى ذلك، في Next.js لا توجد طريقة لتعيين ملفات تعريف الارتباط أو إعادة التحقق من صحة ذاكرة التخزين المؤقت أثناء العرض. هذا أيضًا يثبط من استخدام العروض للطفرات.
على سبيل المثال، لا ينبغي استخدام searchParams
لإجراء تأثيرات جانبية مثل حفظ التغييرات أو تسجيل الخروج. يجب استخدام إجراءات الخادم بدلاً من ذلك.
هذا يعني أن نموذج Next.js لا يستخدم أبدًا طلبات GET للتأثيرات الجانبية عند استخدامه كما هو مقصود. يساعد هذا في تجنب مصدر كبير لمشكلات CSRF.
يدعم Next.js معالجات المسار المخصصة (route.tsx
)، والتي يمكنها تعيين ملفات تعريف الارتباط على GET. تعتبر هذه بمثابة مخرج طوارئ وليست جزءًا من النموذج العام. يجب أن توافق هذه بشكل صريح على قبول طلبات GET. لا يوجد معالج شامل قد يتلقى طلبات GET عن طريق الخطأ. إذا قررت إنشاء معالج GET مخصص، فقد تحتاج هذه إلى تدقيق إضافي.
كتابة
الطريقة المثلى لإجراء عمليات الكتابة أو الطفرات في Next.js App Router هي استخدام إجراءات الخادم.
'use server';
export function logout() {
cookies().delete('AUTH_TOKEN');
}
تعليق "use server"
يعرض نقطة نهاية تجعل جميع الدوال المصدرة قابلة للاستدعاء من قبل العميل. المعرفات حاليًا هي تجزئة لموقع الكود المصدري. طالما حصل المستخدم على مقبض معرف إجراء، يمكنه استدعائه بأي وسيطات.
نتيجة لذلك، يجب أن تبدأ هذه الدوال دائمًا بالتحقق من أن المستخدم الحالي مسموح له باستدعاء هذا الإجراء. يجب أيضًا أن تتحقق الدوال من سلامة كل وسيطة. يمكن القيام بذلك يدويًا أو باستخدام أداة مثل zod
.
"use server";
export async function deletePost(id: number) {
if (typeof id !== 'number') {
// لا يتم فرض تعليقات TypeScript لذلك
// قد نحتاج إلى التحقق من أن المعرف هو ما نعتقد
// أنه هو.
throw new Error();
}
const user = await getCurrentUser();
if (!canDeletePost(user, id)) {
throw new Error();
}
...
}
الإغلاقات (Closures)
يمكن أيضًا ترميز إجراءات الخادم (Server Actions) في الإغلاقات (Closures). هذا يسمح بربط الإجراء بلقطة من البيانات المستخدمة وقت التصيير بحيث يمكنك استخدامها عند استدعاء الإجراء:
export default function Page() {
const publishVersion = await getLatestVersion();
async function publish() {
"use server";
if (publishVersion !== await getLatestVersion()) {
throw new Error('The version has changed since pressing publish');
}
...
}
return <button action={publish}>Publish</button>;
}
يجب إرسال لقطة الإغلاق إلى العميل وإعادتها عند استدعاء الخادم.
في Next.js 14، يتم تشفير المتغيرات المغلقة مع معرف الإجراء قبل إرسالها إلى العميل. بشكل افتراضي، يتم إنشاء مفتاح خاص تلقائيًا أثناء بناء مشروع Next.js. كل إعادة بناء تولد مفتاحًا خاصًا جديدًا مما يعني أنه يمكن استدعاء كل إجراء خادم (Server Action) لإصدار بناء محدد فقط. قد ترغب في استخدام حماية الانحراف (Skew Protection) لضمان استدعاء الإصدار الصحيح أثناء إعادة النشر.
إذا كنت بحاجة إلى مفتاح يتغير بشكل متكرر أو يظل ثابتًا عبر عدة عمليات بناء، يمكنك تكوينه يدويًا باستخدام متغير البيئة NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
.
من خلال تشفير جميع المتغيرات المغلقة، تتجنب الكشف عن أي أسرار فيها عن طريق الخطأ. ومن خلال توقيعها، يصبح من الصعب على المهاجم العبث بإدخال الإجراء.
بديل آخر لاستخدام الإغلاقات هو استخدام دالة .bind(...)
في JavaScript. هذه ليست مشفرة. وهذا يوفر خيارًا للإلغاء من أجل الأداء وهو أيضًا متسق مع .bind()
على العميل.
async function deletePost(id: number) {
"use server";
// التحقق من المعرف وإمكانية الحذف
...
}
export async function Page({ slug }) {
const post = await getPost(slug);
return <button action={deletePost.bind(null, post.id)}>
Delete
</button>;
}
المبدأ هو أن قائمة وسائط إجراءات الخادم ("use server"
) يجب أن تعامل دائمًا على أنها معادية ويجب التحقق من الإدخال.
هجمات CSRF
يمكن استدعاء جميع إجراءات الخادم (Server Actions) بواسطة <form>
عادي، مما قد يعرضها لهجمات CSRF. خلف الكواليس، يتم دائمًا تنفيذ إجراءات الخادم باستخدام POST وهذه هي طريقة HTTP الوحيدة المسموح بها لاستدعائها. هذا وحده يمنع معظم ثغرات CSRF في المتصفحات الحديثة، خاصة بسبب كون ملفات تعريف الارتباط Same-Site هي الافتراضية.
كحماية إضافية، تقارن إجراءات الخادم في Next.js 14 أيضًا رأس Origin
برأس Host
(أو X-Forwarded-Host
). إذا لم يتطابقا، سيتم رفض الإجراء. بمعنى آخر، يمكن استدعاء إجراءات الخادم فقط على نفس المضيف الذي يستضيف الصفحة. المتصفحات القديمة جدًا وغير المدعومة والتي لا تدعم رأس Origin
قد تكون معرضة للخطر.
لا تستخدم إجراءات الخادم رموز CSRF، لذا فإن تعقيم HTML أمر بالغ الأهمية.
عند استخدام معالجات المسار المخصصة (route.tsx
) بدلاً من ذلك، قد تكون هناك حاجة إلى تدقيق إضافي حيث يجب تنفيذ حماية CSRF يدويًا هناك. تنطبق القواعد التقليدية هناك.
معالجة الأخطاء (Error Handling)
تحدث الأخطاء. عند طرح الأخطاء على الخادم، يتم إعادة طرحها في النهاية في كود العميل ليتم معالجتها في واجهة المستخدم. قد تحتوي رسائل الخطأ وتتبع المكدس على معلومات حساسة. على سبيل المثال: [رقم البطاقة الائتمانية] ليس رقم هاتف صالح
.
في وضع الإنتاج، لا يرسل React الأخطاء أو الوعود المرفوضة إلى العميل. بدلاً من ذلك، يتم إرسال تجزئة تمثل الخطأ. يمكن استخدام هذه التجزئة لربط عدة أخطاء متشابهة معًا وربط الخطأ بسجلات الخادم. يستبدل React رسالة الخطأ برسالة عامة خاصة به.
في وضع التطوير، لا تزال أخطاء الخادم تُرسل كنص عادي إلى العميل للمساعدة في تصحيح الأخطاء.
من المهم دائمًا تشغيل Next.js في وضع الإنتاج لأحمال العمل الإنتاجية. وضع التطوير لا يحسن الأمان والأداء.
المسارات المخصصة والوسيط (Custom Routes and Middleware)
معالجات المسار المخصصة والوسيط (Middleware) تعتبر مخارج منخفضة المستوى للميزات التي لا يمكن تنفيذها باستخدام أي وظيفة مضمنة أخرى. هذا يفتح أيضًا الباب أمام مشاكل محتملة يحمي منها الإطار بطريقة أخرى. مع القوة الكبيرة تأتي المسؤولية الكبيرة.
كما ذكرنا أعلاه، يمكن لمسارات route.tsx
تنفيذ معالجات GET وPOST المخصصة التي قد تعاني من مشاكل CSRF إذا لم يتم تنفيذها بشكل صحيح.
يمكن استخدام الوسيط (Middleware) للحد من الوصول إلى صفحات معينة. عادةً ما يكون من الأفضل القيام بذلك باستخدام قائمة السماح بدلاً من قائمة الحظر. وذلك لأنه قد يكون من الصعب معرفة جميع الطرق المختلفة للوصول إلى البيانات، مثل ما إذا كان هناك إعادة كتابة أو طلب عميل.
على سبيل المثال، من الشائع التفكير فقط في صفحة HTML. يدعم Next.js أيضًا التنقل على العميل الذي يمكنه تحميل حمولات RSC/JSON. في موجه الصفحات (Pages Router)، كان هذا في عنوان URL مخصص سابقًا.
لتسهيل كتابة المطابقات، يستخدم موجه التطبيق (App Router) في Next.js دائمًا عنوان URL العادي للصفحة لكل من HTML الأولي، التنقل على العميل وإجراءات الخادم. يستخدم التنقل على العميل معلمة البحث ?_rsc=...
ككسر للتخزين المؤقت.
تعيش إجراءات الخادم على الصفحة التي تستخدمها وبالتالي ترث نفس تحكم الوصول. إذا سمح الوسيط (Middleware) بقراءة صفحة، يمكنك أيضًا استدعاء الإجراءات على تلك الصفحة. للحد من الوصول إلى إجراءات الخادم على صفحة، يمكنك حظر طريقة HTTP POST على تلك الصفحة.
التدقيق (Audit)
إذا كنت تقوم بتدقيق مشروع موجه التطبيق (App Router) في Next.js، فإليك بعض الأشياء التي نوصي بالتركيز عليها:
- طبقة الوصول إلى البيانات. هل هناك ممارسة راسخة لطبقة وصول إلى البيانات معزولة؟ تحقق من عدم استيراد حزم قواعد البيانات ومتغيرات البيئة خارج طبقة الوصول إلى البيانات.
- ملفات
"use client"
. هل تتوقع خاصيات المكونات (Component props) بيانات خاصة؟ هل توقيعات الأنواع واسعة جدًا؟ - ملفات
"use server"
. هل يتم التحقق من وسائط الإجراء في الإجراء أو داخل طبقة الوصول إلى البيانات؟ هل يتم إعادة تفويض المستخدم داخل الإجراء؟ /[param]/
. المجلدات ذات الأقواس هي إدخال مستخدم. هل يتم التحقق من المعلمات؟middleware.tsx
وroute.tsx
لديهما الكثير من القوة. اقضِ وقتًا إضافيًا في تدقيق هذه باستخدام التقنيات التقليدية. قم بإجراء اختبار الاختراق أو فحص الثغرات بانتظام أو بالتنسيق مع دورة حياة تطوير البرمجيات لفريقك.