تلويث (taint)

الاستخدام

خيار taint يمكّن دعم واجهات برمجة تطبيقات React التجريبية لتلويث الكائنات والقيم. هذه الميزة تساعد في منع تمرير البيانات الحساسة عن طريق الخطأ إلى العميل. عند التمكين، يمكنك استخدام:

معلومة مفيدة: تفعيل هذا الخيار يُفعّل أيضًا قناة 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.