تلويث (taint)
الاستخدام
خيار taint
يمكّن دعم واجهات برمجة تطبيقات React التجريبية لتلويث الكائنات والقيم. هذه الميزة تساعد في منع تمرير البيانات الحساسة عن طريق الخطأ إلى العميل. عند التمكين، يمكنك استخدام:
experimental_taintObjectReference
لتلويث مراجع الكائنات.experimental_taintUniqueValue
لتلويث القيم الفريدة.
معلومة مفيدة: تفعيل هذا الخيار يُفعّل أيضًا قناة
experimental
في React لمجلدapp
.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
taint: true,
},
}
export default nextConfig
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
taint: true,
},
}
module.exports = nextConfig
تحذير: لا تعتمد على واجهة برمجة التطبيقات الخاصة بالتلويث كآلية وحيدة لمنع كشف البيانات الحساسة للعميل. راجع توصيات الأمان.
تسمح واجهات برمجة التطبيقات الخاصة بالتلويث بأن تكون دفاعيًا، من خلال تحديد البيانات التي لا يُسمح بمرورها عبر حدود الخادم-العميل بشكل صريح وإعلاني. عندما يتم تمرير كائن أو قيمة عبر حدود الخادم-العميل، يطرح React خطأ.
هذا مفيد في الحالات التالية:
- عندما تكون طرق قراءة البيانات خارج نطاق سيطرتك
- عندما تضطر للعمل مع أشكال بيانات حساسة لم تقم بتعريفها
- عندما يتم الوصول إلى بيانات حساسة أثناء عرض مكون الخادم (Server Component)
يوصى بنمذجة البيانات وواجهات برمجة التطبيقات بحيث لا يتم إرجاع البيانات الحساسة إلى السياقات التي لا تحتاجها.
محاذير
- يمكن للتلويث تتبع الكائنات فقط عن طريق المرجع. نسخ كائن ينشئ نسخة غير ملوثة، مما يفقد كل الضمانات التي توفرها واجهة برمجة التطبيقات. ستحتاج إلى تلويث النسخة.
- لا يمكن للتلويث تتبع البيانات المشتقة من قيمة ملوثة. ستحتاج أيضًا إلى تلويث القيمة المشتقة.
- تظل القيم ملوثة طالما أن مرجع عمرها ضمن النطاق. راجع مرجع معلمات
experimental_taintUniqueValue
لمزيد من المعلومات.
أمثلة
تلويث مرجع كائن
في هذه الحالة، تقوم الدالة getUserDetails
بإرجاع بيانات حول مستخدم معين. نقوم بتلويث مرجع كائن المستخدم، بحيث لا يمكنه عبور حدود الخادم-العميل. على سبيل المثال، بافتراض أن UserCard
هو مكون عميل (Client Component).
import { experimental_taintObjectReference } from 'react'
function getUserDetails(id: string): UserDetails {
const user = await db.queryUserById(id)
experimental_taintObjectReference(
'لا تستخدم كامل معلومات المستخدم. بدلاً من ذلك، اختر فقط الحقول التي تحتاجها.',
user
)
return user
}
import { experimental_taintObjectReference } from 'react'
function getUserDetails(id) {
const user = await db.queryUserById(id)
experimental_taintObjectReference(
'لا تستخدم كامل معلومات المستخدم. بدلاً من ذلك، اختر فقط الحقول التي تحتاجها.',
user
)
return user
}
لا يزال بإمكاننا الوصول إلى الحقول الفردية من كائن userDetails
الملوث.
export async function ContactPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const userDetails = await getUserDetails(id)
return (
<UserCard
firstName={userDetails.firstName}
lastName={userDetails.lastName}
/>
)
}
export async function ContactPage({ params }) {
const { id } = await params
const userDetails = await getUserDetails(id)
return (
<UserCard
firstName={userDetails.firstName}
lastName={userDetails.lastName}
/>
)
}
الآن، تمرير الكائن بالكامل إلى مكون العميل سيثير خطأ.
export async function ContactPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const userDetails = await getUserDetails(id)
// يثير خطأ
return <UserCard user={userDetails} />
}
export async function ContactPage({ params }) {
const { id } = await params
const userDetails = await getUserDetails(id)
// يثير خطأ
return <UserCard user={userDetails} />
}
تلويث قيمة فريدة
في هذه الحالة، يمكننا الوصول إلى تكوين الخادم عن طريق انتظار استدعاءات config.getConfigDetails
. ومع ذلك، يحتوي تكوين النظام على SERVICE_API_KEY
الذي لا نريد كشفه للعملاء.
يمكننا تلويث قيمة config.SERVICE_API_KEY
.
import { experimental_taintUniqueValue } from 'react'
function getSystemConfig(): SystemConfig {
const config = await config.getConfigDetails()
experimental_taintUniqueValue(
'لا تمرر رموز التكوين إلى العميل',
config,
config.SERVICE_API_KEY
)
return config
}
import { experimental_taintUniqueValue } from 'react'
function getSystemConfig() {
const config = await config.getConfigDetails()
experimental_taintUniqueValue(
'لا تمرر رموز التكوين إلى العميل',
config,
config.SERVICE_API_KEY
)
return config
}
لا يزال بإمكاننا الوصول إلى خصائص أخرى من كائن systemConfig
.
export async function Dashboard() {
const systemConfig = await getSystemConfig()
return <ClientDashboard version={systemConfig.SERVICE_API_VERSION} />
}
ومع ذلك، تمرير SERVICE_API_KEY
إلى ClientDashboard
سيثير خطأ.
export async function Dashboard() {
const systemConfig = await getSystemConfig()
// يقوم أحدهم بخطأ في طلب سحب (PR)
const version = systemConfig.SERVICE_API_KEY
return <ClientDashboard version={version} />
}
لاحظ أنه حتى مع إعادة تعيين systemConfig.SERVICE_API_KEY
إلى متغير جديد، فإن تمريره إلى مكون العميل سيظل يثير خطأ.
بينما سيتم كشف قيمة مشتقة من قيمة فريدة ملوثة إلى العميل.
export async function Dashboard() {
const systemConfig = await getSystemConfig()
// يقوم أحدهم بخطأ في طلب سحب (PR)
const version = `version::${systemConfig.SERVICE_API_KEY}`
return <ClientDashboard version={version} />
}
نهج أفضل هو إزالة SERVICE_API_KEY
من البيانات التي تُرجعها getSystemConfig
.