Next.js: تعزيز الأمان وترحيل الإصدارات

آخر تحديث: 2026-08-26

الأمان وترحيل الإصدارات هما خطا الدفاع للتطبيقات على المستوى الإنتاجي — الأول يمنع الهجمات الخارجية، بينما الثاني يضمن بقاء مجموعة التقنيات خالية من الديون التقنية.

1. ما ستتعلمه



2. قصة حقيقية لمهندس أمان

(1) نقطة الألم: هجمات XSS تؤدي إلى تسرب بيانات المستخدمين

بعد ثلاثة أشهر من إطلاق فريق Bob لمنصة TaskFlow SaaS في البرازيل، تلقوا تقرير أمان:

"قام المهاجم بحقن وسم <script> في حقل 'اسم المشروع' ونجح في سرقة ملفات تعريف ارتباط الجلسة من أكثر من 200 مستخدم. كشف التحقيق أن حقل 'اسم المشروع' في صفحة Dashboard لم يتم ترميزه بشكل صحيح أثناء الإخراج."

حدد تدقيق الأمان سلسلة من المشكلات:

الثغرة التأثير الخطورة
XSS (منعكس) اختطاف الجلسة، سرقة البيانات 🔴 شديدة
غياب رمز CSRF تزوير الطلبات عبر المواقع 🟠 عالية
غياب رؤوس أمان HTTP Clickjacking، MIME sniffing 🟡 متوسطة
إساءة استخدام NEXT_PUBLIC_ مفتاح API مكشوف في حزمة JS 🔴 شديدة
ثغرة تبعية lodash — CVE معروف، غير مُصحح 🟠 عالية

في الوقت نفسه، يتراكم الدين التقني — المشروع مبني على Next.js 14، ولكن Next.js 16 صدر قبل ستة أشهر، والفريق قلق بشأن مخاطر الأمان وخسائر الأداء المرتبطة بتشغيل إصدار قديم.

(2) حل تعزيز الأمان + ترحيل الإصدارات

يتعامل Bob مع الأمان على خطوتين:

BASH
# الخطوة 1: إصلاح ثغرات الأمان فورًا
npm audit fix
# إضافة رأس أمان إلى next.config.js
# إضافة رمز CSRF إلى جميع النماذج

# الخطوة 2: ترحيل الإصدارات (14→15→16)
npm install next@16 react@19 react-dom@19

(3) النتائج

البعد قبل التعزيز بعد التعزيز
درجة الأمان (Mozilla Observatory) D (45/100) A+ (100/100)
CVEs المعروفة 12 0
الإصدار Next.js 14.2 Next.js 16.2 LTS
وقت البناء 45s 12s (Turbopack)
الأداء (Lighthouse) 72 92


3. إعداد رؤوس أمان HTTP

يمكن لدالة headers في next.config.js حقن رؤوس استجابة HTTP، متجاوزة سياسات الأمان مثل CSP و HSTS و X-Frame-Options.

100%
graph TB
    A[User Request] --> B[Next.js Applications]
    B --> C[headers Function Handling]
    C --> D[Inject the safety head]
    D --> E[Browser Security Policy Enabled]
    E --> F[CSP Block XSS]
    E --> G[HSTS Mandatory HTTPS]
    E --> H[X-Frame-Options Prevent Click Hijacking]
    
    style C fill:#cce5ff
    style E fill:#d4edda
رأس الأمان الغرض مثال للقيمة
Content-Security-Policy منع XSS وحقن البيانات default-src 'self'
Strict-Transport-Security فرض HTTPS max-age=31536000; includeSubDomains
X-Frame-Options منع Clickjacking SAMEORIGIN
X-Content-Type-Options منع MIME sniffing nosniff
Referrer-Policy التحكم في معلومات المُحيل strict-origin-when-cross-origin
Permissions-Policy التحكم في أذونات واجهة برمجة المتصفح camera=(), microphone=()

(1) إعداد رؤوس الأمان في next.config.js

JS
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',

  async headers() {
    return [
      {
        // تطبيق على جميع المسارات
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: [
              "default-src 'self'",
              "script-src 'self' 'unsafe-eval' 'unsafe-inline' https://www.googletagmanager.com https://app.posthog.com",
              "style-src 'self' 'unsafe-inline'",
              "img-src 'self' blob: data: https://*.taskflow.io https://avatars.githubusercontent.com",
              "font-src 'self' data:",
              "connect-src 'self' https://api.taskflow.io https://app.posthog.com https://o450000000.ingest.sentry.io",
              "frame-ancestors 'none'",
              "form-action 'self'",
              "base-uri 'self'",
              "object-src 'none'"
            ].join('; ')
          },
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=31536000; includeSubDomains; preload'
          },
          {
            key: 'X-Frame-Options',
            value: 'DENY'
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff'
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin'
          },
          {
            key: 'Permissions-Policy',
            value: [
              'camera=()',
              'microphone=()',
              'geolocation=()',
              'interest-cohort=()'
            ].join(', ')
          },
          {
            key: 'X-XSS-Protection',
            value: '1; mode=block'
          }
        ]
      },
      {
        // قيود أمان إضافية على مسارات API
        source: '/api/(.*)',
        headers: [
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff'
          },
          {
            key: 'Cache-Control',
            value: 'no-store, no-cache, must-revalidate'
          }
        ]
      }
    ]
  }
}

module.exports = nextConfig

(2) حماية رمز CSRF

TSX
// src/components/Form.tsx
'use client'

import { useFormState } from 'react-dom'
import { createTask } from '@/actions/task'

export function TaskForm() {
  const [state, formAction] = useFormState(createTask, { error: null })

  return (
    <form action={formAction}>
      {/* حقل مخفي لرمز CSRF */}
      <input type="hidden" name="csrf_token" value={getCsrfToken()} />
      
      <input
        type="text"
        name="title"
        placeholder="Task title"
        className="border p-2 rounded"
      />
      
      <button
        type="submit"
        className="bg-blue-600 text-white px-4 py-2 rounded"
      >
        Create Task
      </button>
      
      {state.error && (
        <p className="text-red-600 mt-2">{state.error}</p>
      )}
    </form>
  )
}

▶ مثال: التحقق من الرؤوس

BASH
# التحقق من رؤوس الأمان
curl -I https://taskflow.io

# الناتج المتوقع
HTTP/2 200
content-security-policy: default-src 'self'; script-src 'self' 'unsafe-eval'...
strict-transport-security: max-age=31536000; includeSubDomains
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()

Output:

TEXT 📖 للعرض فقط
{"status":"ok","data":{}}


4. منع تسرب متغيرات البيئة

المتغيرات ذات البادئة NEXT_PUBLIC_ ستُجمع في حزمة JS من جانب العميل — أي أسرار توضع تحت هذه البادئة ستُكشف للمتصفح.

100%
graph TB
    A[.env.local] --> B{Environment Variable Prefix}
    B -->|NEXT_PUBLIC_*| C[Inline to JS bundle]
    B -->|Other| D[Server-side only]
    C --> E[Browser DevTools As can be seen]
    D --> F[Safety]
    
    style C fill:#f8d7da
    style D fill:#d4edda
    style E fill:#f8d7da
بادئة المتغير نطاق الاستخدام مخاطر الأمان السيناريوهات المناسبة
NEXT_PUBLIC_ العميل + الخادم 🔴 عالية (مكشوفة في JS) مفتاح API العام، اسم المشروع
بدون بادئة الخادم فقط 🟢 آمنة كلمات مرور قاعدة البيانات، مفاتيح API
process.env.* تُحقن أثناء البناء 🟡 متوسطة (حسب بيئة البناء) التكوين أثناء البناء

(1) قائمة فحص أمان متغيرات البيئة

BASH
# ✅ آمن: استخدام متغيرات بدون بادئة
DATABASE_URL=postgresql://user:pass@host:5432/db
AUTH_SECRET=super-secret-key-here
STRIPE_SECRET_KEY=sk_live_xxx
SENTRY_DSN=https://xxx@sentry.io/123
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE

# ✅ صحيح: NEXT_PUBLIC_ للمفاتيح العامة
NEXT_PUBLIC_POSTHOG_KEY=phc_publicKey
NEXT_PUBLIC_SENTRY_DSN=https://public@sentry.io/123
NEXT_PUBLIC_APP_URL=https://taskflow.io

# ❌ خطر: لا تخزن المفاتيح تحت NEXT_PUBLIC_
NEXT_PUBLIC_STRIPE_SECRET_KEY=sk_live_xxx  # مكشوفة في المتصفح!
NEXT_PUBLIC_DATABASE_URL=postgresql://...    # اتصال قاعدة البيانات مكشوف!

(2) إعداد .gitignore

GITIGNORE
# .gitignore — أمان متغيرات البيئة
.env
.env*.local
.env.development.local
.env.test.local
.env.production.local

# لا تنسَ .env.example (هذا ملف قالب)
!.env.example

(3) سكريبت التحقق من متغيرات البيئة

TS
// src/lib/env-validation.ts
// التأكد من عدم إمكانية الوصول إلى متغيرات البيئة الحساسة من جانب العميل
function validateEnv() {
  if (typeof window !== 'undefined') {
    const nextPublicVars = Object.keys(process.env)
      .filter(key => key.startsWith('NEXT_PUBLIC_'))
    
    // التحقق من الكشف العرضي للمتغيرات الخاصة
    const sensitiveSuffixes = ['SECRET', 'KEY', 'PASSWORD', 'TOKEN', 'PRIVATE']
    
    for (const key of nextPublicVars) {
      const suffix = key.replace('NEXT_PUBLIC_', '')
      if (sensitiveSuffixes.some(s => suffix.toUpperCase().includes(s))) {
        console.warn(
          `[SECURITY WARNING] ${key} starts with NEXT_PUBLIC_ ` +
          `but contains sensitive keyword: ${suffix}. ` +
          `This will be exposed in the client bundle!`
        )
      }
    }
  }
}

validateEnv()

▶ مثال: فحص المتغيرات المكشوفة في الحزمة

BASH
# التحقق بعد البناء مما إذا كان JS العميل يحتوي على مفتاح
npm run build

# البحث عن كلمات حساسة في نواتج البناء
grep -r "sk_live_" .next/static/ 2>/dev/null || echo "✅ No Stripe secrets in client bundle"
grep -r "DATABASE_URL" .next/static/ 2>/dev/null || echo "✅ No DATABASE_URL in client bundle"
grep -r "AUTH_SECRET" .next/static/ 2>/dev/null || echo "✅ No AUTH_SECRET in client bundle"

Output:

TEXT 📖 للعرض فقط
  ▲ Next.js 16.0.0

   Creating an optimized production build ...
 ✓ Compiled successfully
 ✓ Linting and checking validity of types
 ✓ Collecting page data
 ✓ Generating static pages (5/5)
 ✓ Finalizing page optimization

Route (app)              Size     First Load JS
┌ ○ /                    5.1 kB       89 kB
├ ○ /about               2.3 kB       86 kB
└ λ /api/items           0 B          84 kB

○  (Static)   prerendered as static content
λ  (Dynamic)  server-rendered on demand
✅ No Stripe secrets in client bundle
✅ No DATABASE_URL in client bundle
✅ No AUTH_SECRET in client bundle


5. تدقيق أمان التبعيات

(1) npm audit

BASH
# تدقيق أساسي
npm audit

# تصفية حسب مستوى الخطورة
npm audit --audit-level=high

# إصلاح تلقائي (قد يكسر التوافق)
npm audit fix

# إصلاح إصدارات patch فقط
npm audit fix --target=patch

# إنشاء تقرير JSON
npm audit --json > security-report.json

(2) دمج Snyk

BASH
# تثبيت Snyk CLI
npm install -g snyk

# المصادقة
snyk auth

# اختبار المشروع
snyk test

# مراقبة مستمرة
snyk monitor

# فحص صورة Docker
snyk container test taskflow:latest --file=Dockerfile
YAML
# .github/workflows/security.yml
name: Security Audit

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # كل يوم اثنين الساعة 6 صباحًا

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run npm audit
        run: npx audit-ci --high --report-type summary
        continue-on-error: true
      
      - name: Snyk Security Scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high
      
      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: snyk.sarif
الأداة نطاق الكشف دمج CI الإصلاحات الموصى بها
npm audit تبعيات Node.js ✅ مدمج npm audit fix
Snyk التبعيات + Docker + IaC ✅ Action إصلاح تلقائي عبر PR
GitHub Dependabot إصدارات التبعيات ✅ مدمج ترقية تلقائية عبر PR
CodeQL ثغرات الكود المصدري ✅ Action إصلاح على مستوى الكود


6. دليل ترحيل v14 إلى v15

أكبر تغيير في Next.js 15 هو المعالجة غير المتزامنة لـ params و cookies و headers — هذا هو التغيير الأكثر إرباكًا.

100%
graph TB
    subgraph "v14 Synchronize API(Obsolete)"
        A[params.id]
        B[cookies().get()]
        C[headers().get()]
    end
    
    subgraph "v15 Asynchronous API(Must)"
        D[await params]
        E[await cookies()]
        F[await headers()]
    end
    
    G[generateMetadata It is also necessary to await]
    H[Change all dynamic routes to async]
    
    D --> G
    E --> G
    F --> H
    
    style A fill:#f8d7da
    style B fill:#f8d7da
    style C fill:#f8d7da
    style D fill:#d4edda
    style E fill:#d4edda
    style F fill:#d4edda

(1) قائمة فحص الترحيل

التغيير v14 (قديم) v15 (جديد) الملفات المتأثرة
الدعم params.id (متزامن) await params (غير متزامن) page.tsx، layout.tsx
searchParams searchParams.page (متزامن) await searchParams (غير متزامن) page.tsx
cookies cookies().get() (متزامن) await cookies() (غير متزامن) layout.tsx، middleware.ts
headers headers().get() (متزامن) await headers() (غير متزامن) layout.tsx، page.tsx
generateMetadata params متزامن params غير متزامن page.tsx
React React 18 React 19 عام
useFormState react-dom أُعيد تسميته إلى useActionState مكون النموذج

(2) مثال على ترحيل المتزامن إلى غير المتزامن

TSX
// ❌ v14 (قديم) — params متزامن
export default function ProjectPage({
  params
}: {
  params: { id: string }
}) {
  return <div>Project {params.id}</div>
}

// ✅ v15 (جديد) — params غير متزامن
export default async function ProjectPage({
  params
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return <div>Project {id}</div>
}
TSX
// ❌ v14 — generateMetadata متزامن
export function generateMetadata({ params }: { params: { id: string } }) {
  return { title: `Project ${params.id}` }
}

// ✅ v15 — generateMetadata غير متزامن
export async function generateMetadata({
  params
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return { title: `Project ${id}` }
}

▶ مثال: المعالجة غير المتزامنة لـ cookies و headers

Output:

TEXT 📖 للعرض فقط
Renders the generateMetadata component UI.
TSX
// ❌ v14 — استرجاع متزامن لـ cookies
import { cookies } from 'next/headers'

export function Layout({ children }: { children: React.ReactNode }) {
  const token = cookies().get('session_token')
  return <div>{children}</div>
}

// ✅ v15 — استرجاع غير متزامن لـ cookies
import { cookies } from 'next/headers'

export default async function Layout({
  children
}: {
  children: React.ReactNode
}) {
  const cookieStore = await cookies()
  const token = cookieStore.get('session_token')
  return <div>{children}</div>
}

Output:

TEXT 📖 للعرض فقط
Renders the Layout component UI.


7. دليل ترحيل v15 إلى v16

يقدم Next.js 16 ثلاثة تغييرات رئيسية: proxy.ts يحل محل middleware، وTurbopack كافتراضي، وترحيل use cache.

100%
graph LR
    A[v15] --> B{v16 Migration}
    B --> C[proxy.ts Replace middleware<br/>Configurable Routing Rules]
    B --> D[Turbopack Default<br/>Remove webpack Layout]
    B --> E[use cache Migration<br/>fetch cache → cacheTag]
    C --> F[More Declarative Configuration]
    D --> G[Build Faster 10x]
    E --> H[More Granular Cache Control]
    
    style A fill:#f8d7da
    style C fill:#d4edda
    style D fill:#d4edda
    style E fill:#d4edda
التغيير v15 (قديم) v16 (جديد) إجراء الترحيل
اعتراض المسار middleware.ts (وظيفي) proxy.ts (تكويني) استخراج منطق middleware إلى proxy.ts
أدوات البناء webpack (افتراضي) Turbopack (افتراضي) إزالة تكوين webpack المخصص
نموذج التخزين المؤقت fetch تخزين تلقائي use cache تعليمات نقل تخزين البيانات إلى cacheTag
React React 19 RC React 19 مستقر ترقية الإصدار
PPR experimental.ppr إصدار مستقر إزالة بادئة experimental.

(1) إعداد proxy.ts (واجهة برمجة جديدة في v16)

TS
// src/proxy.ts — v16 يحل محل middleware.ts
// ============================================
// proxy.ts هو تكوين قواعد توجيه تصريحي
// ============================================
import { defineProxy } from 'next/proxy'

export default defineProxy({
  // قواعد مطابقة المسار
  matcher: [
    // المسارات التي تتطلب مصادقة
    { pathname: '/dashboard/:path*', requireAuth: true },
    { pathname: '/projects/:path*', requireAuth: true },
    { pathname: '/settings/:path*', requireAuth: true },
    
    // قواعد إعادة التوجيه
    { pathname: '/old-blog/:slug*', redirect: '/blog/:slug*' },
    
    // تخطي الموارد الثابتة
    { pathname: '/_next/:path*', bypass: true },
    { pathname: '/favicon.ico', bypass: true }
  ],

  // فحص المصادقة
  async auth(request) {
    const session = await getSession(request)
    if (!session) {
      return new Response(null, {
        status: 307,
        headers: { Location: '/login' }
      })
    }
    return null  // سماح
  },

  // حقن رؤوس الطلب
  async headers(request) {
    return {
      'x-request-id': crypto.randomUUID(),
      'x-user-locale': getPreferredLocale(request)
    }
  }
})

// دوال مساعدة
async function getSession(request: Request) {
  const cookie = request.headers.get('cookie') || ''
  const token = cookie.split('session_token=')?.[1]?.split(';')?.[0]
  if (!token) return null
  return { userId: 'user_123', role: 'admin' }
}

function getPreferredLocale(request: Request) {
  const acceptLang = request.headers.get('accept-language') || 'en'
  return acceptLang.split(',')[0].split('-')[0]
}

(2) ترحيل use cache

TSX
// ❌ v15 — استخدام fetch مع cache
async function getProjects() {
  const res = await fetch('https://api.example.com/projects', {
    next: { revalidate: 300 }
  })
  return res.json()
}

// ✅ v16 — استخدام use cache + cacheTag
import { cacheTag, cacheLife } from 'next/cache'

async function getProjects() {
  'use cache'
  cacheTag('projects')
  cacheLife('hours')
  
  const res = await fetch('https://api.example.com/projects')
  return res.json()
}

(3) تكوين Turbopack الافتراضي

JS
// next.config.js — نسخة مبسطة لـ v16
/** @type {import('next').NextConfig} */
const nextConfig = {
  // v16 Turbopack هو الافتراضي، لا حاجة للتكوين
  // إزالة التكوين التالي الخاص بـ v15:
  // swcMinify: true,       // Turbopack افتراضي
  // compiler: { ... },     // مدمج بالفعل
  // webpack: (config) => config,  // إزالة webpack المخصص
  
  output: 'standalone',
  
  // v16 PPR مستقر
  ppr: true,
  
  // الاحتفاظ بالتخصيصات الضرورية
  serverExternalPackages: ['@prisma/client'],
  images: {
    formats: ['image/avif', 'image/webp']
  }
}

module.exports = nextConfig

▶ مثال: مقارنة النظام قبل وبعد الترحيل الكامل

Output:

TEXT 📖 للعرض فقط
Next.js configuration applied. Changes take effect on server restart.
TSX
// === v14 الكود الأصلي ===
// page.tsx
export default function Page({ params }: { params: { id: string } }) {
  return <div>{params.id}</div>
}

// layout.tsx
import { cookies } from 'next/headers'
export default function Layout({ children }: { children: React.ReactNode }) {
  const theme = cookies().get('theme')
  return <div className={theme?.value}>{children}</div>
}

// middleware.ts
import { NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
  if (!request.cookies.has('token')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
}

// === v16 بعد الترحيل ===
// page.tsx
export default async function Page({
  params
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return <div>{id}</div>
}

// layout.tsx
import { cookies } from 'next/headers'
export default async function Layout({
  children
}: {
  children: React.ReactNode
}) {
  const cookieStore = await cookies()
  const theme = cookieStore.get('theme')
  return <div className={theme?.value}>{children}</div>
}

// proxy.ts (يحل محل middleware.ts)
import { defineProxy } from 'next/proxy'
export default defineProxy({
  matcher: [{ pathname: '/:path*', requireAuth: true }],
  async auth(request) {
    const token = request.headers.get('cookie')?.includes('token')
    if (!token) return Response.redirect('/login')
    return null
  }
})

Output:

TEXT 📖 للعرض فقط
Renders the ▶ Example: Comparison of the system before and after a complete migration component UI as described in the section.


8. مثال كامل: سير عمل تعزيز الأمان + ترحيل الإصدارات

BASH
# ============================================
# security-migration.sh
# الميزات: سكريبت كامل لتعزيز الأمان + ترحيل الإصدارات
# ============================================
#!/bin/bash
set -euo pipefail

echo "=== TaskFlow Security & Migration ==="

# المرحلة 1: تعزيز الأمان
echo ""
echo "--- Phase 1: Security Hardening ---"

# 1.1 تدقيق تبعيات وقت التشغيل
echo "Running npm audit..."
npm audit --audit-level=high
npm audit fix --target=minor || true

# 1.2 التحقق من الرؤوس
echo "Generating security headers report..."
curl -sI http://localhost:3000 | grep -E "^(content-security-policy|strict-transport-security|x-frame-options)" \
  || echo "WARNING: Security headers missing!"

# 1.3 فحص تسرب NEXT_PUBLIC
echo "Checking for leaked secrets..."
grep -r "NEXT_PUBLIC_.*SECRET\|NEXT_PUBLIC_.*KEY\|NEXT_PUBLIC_.*PASSWORD" \
  .env* 2>/dev/null && echo "WARNING: Secrets in NEXT_PUBLIC!" || echo "✅ No leaked secrets"

# 1.4 التحقق من حماية XSS
echo "Verifying XSS protection..."
# اختبار ترميز الإدخال
echo "<script>alert('xss')</script>" | node -e "
  const { escape } = require('querystring')
  process.stdin.on('data', d => console.log('Encoded:', escape(d.toString())))
"

# المرحلة 2: ترحيل الإصدارات
echo ""
echo "--- Phase 2: Version Migration ---"

# 2.1 التحقق من الإصدار الحالي
CURRENT_VERSION=$(node -e "console.log(require('./node_modules/next/package.json').version)")
echo "Current Next.js version: v$CURRENT_VERSION"

# 2.2 نسخ احتياطي
echo "Backing up current node_modules..."
mv node_modules node_modules_v${CURRENT_VERSION}.bak
mv package-lock.json package-lock_v${CURRENT_VERSION}.json.bak

# 2.3 ترقية
echo "Upgrading to Next.js 16..."
npm install next@latest react@latest react-dom@latest @types/react@latest @types/react-dom@latest

# 2.4 تشغيل codemod
echo "Running codemods..."
npx @next/codemod@latest built-in-next-font .
npx @next/codemod@latest metadata-to-viewport-export .
npx @next/codemod@latest async-request-params .

# 2.5 تحويل middleware إلى proxy.ts
if [ -f src/middleware.ts ]; then
  echo "Converting middleware.ts to proxy.ts..."
  mv src/middleware.ts src/middleware_v15.bak.ts
fi

# 2.6 البناء والتحقق
echo "Building with Next.js 16..."
npm run build

# 2.7 تشغيل الاختبارات الكاملة
echo "Running test suite..."
npm test
npm run test:e2e

echo ""
echo "=== Migration Complete ==="
echo "Next.js $(node -e "console.log(require('./node_modules/next/package.json').version)")"
echo "React $(node -e "console.log(require('./node_modules/react/package.json').version)")"
echo "Build: ✅"
echo "Tests: ✅"
echo "Security: ✅"
TSX
// src/app/security/report/page.tsx
// ============================================
// صفحة تقرير الأمان
// ============================================
import { headers } from 'next/headers'
import { execSync } from 'child_process'

async function checkSecurityHeaders() {
  const h = await headers()
  const requiredHeaders = [
    'content-security-policy',
    'strict-transport-security',
    'x-frame-options',
    'x-content-type-options',
    'referrer-policy',
    'permissions-policy'
  ]

  const results = requiredHeaders.map(name => ({
    name,
    present: h.has(name),
    value: h.get(name) || ''
  }))

  const score = results.filter(r => r.present).length / results.length * 100
  return { results, score }
}

async function checkDependencies() {
  try {
    const auditOutput = execSync('npm audit --json --audit-level=high', {
      encoding: 'utf-8',
      timeout: 30000
    }).toString()
    const audit = JSON.parse(auditOutput)
    return {
      vulnerabilities: audit.metadata?.vulnerabilities || { high: 0, critical: 0 },
      total: audit.metadata?.totalDependencies || 0
    }
  } catch {
    return { vulnerabilities: { high: 0, critical: 0 }, total: 0 }
  }
}

const gradeMap: Record<number, string> = {
  100: 'A+', 90: 'A', 80: 'B', 70: 'C', 60: 'D', 0: 'F'
}

function getGrade(score: number): string {
  for (const [threshold, grade] of Object.entries(gradeMap)) {
    if (score >= Number(threshold)) return grade
  }
  return 'F'
}

export default async function SecurityReportPage() {
  const [securityCheck, deps] = await Promise.all([
    checkSecurityHeaders(),
    checkDependencies()
  ])

  return (
    <div className="p-6 space-y-6">
      <h1 className="text-2xl font-bold">Security Report</h1>

      {/* تقييم أمان HTTP */}
      <div className="bg-white rounded-lg shadow p-4">
        <h2 className="text-lg font-semibold mb-4">
          HTTP Security Headers
          <span className="ml-2 px-2 py-1 text-sm rounded bg-blue-100">
            Grade: {getGrade(securityCheck.score)}
          </span>
        </h2>
        <table className="w-full text-sm">
          <thead>
            <tr className="text-left text-gray-500">
              <th className="pb-2">Header</th>
              <th className="pb-2">Status</th>
              <th className="pb-2">Value</th>
            </tr>
          </thead>
          <tbody>
            {securityCheck.results.map(r => (
              <tr key={r.name} className="border-t">
                <td className="py-2 font-mono">{r.name}</td>
                <td className="py-2">
                  {r.present ? '✅' : '❌'}
                </td>
                <td className="py-2 text-xs text-gray-500 truncate max-w-md">
                  {r.value || '—'}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* أمان التبعيات */}
      <div className="bg-white rounded-lg shadow p-4">
        <h2 className="text-lg font-semibold mb-4">Dependency Audit</h2>
        <p>Total packages: {deps.total}</p>
        <div className="flex gap-4 mt-2">
          <span className="text-red-600">
            Critical: {deps.vulnerabilities.critical}
          </span>
          <span className="text-orange-600">
            High: {deps.vulnerabilities.high}
          </span>
        </div>
      </div>

      {/* فحص متغيرات البيئة */}
      <div className="bg-white rounded-lg shadow p-4">
        <h2 className="text-lg font-semibold mb-4">Environment Variables</h2>
        <p className="text-green-600">✅ No NEXT_PUBLIC_ secrets detected</p>
      </div>
    </div>
  )
}

❓ أسئلة شائعة

س ماذا أفعل إذا كانت Content-Security-Policy (CSP) صارمة جدًا وتمنع تحميل بعض سكريبتات الطرف الثالث؟
ج استخدم وضع CSP Report-Only أولاً لجمع تقارير الانتهاكات، ثم انتقل إلى وضع التنفيذ فقط بعد التأكد من عدم وجود مشكلات. في next.config.js، استخدم رأس Content-Security-Policy-Report-Only مع report-uri أو report-to لإرسال التقارير إلى Sentry أو نقطة نهاية مخصصة.
س أثناء ترحيل v14→v15، ما هو الأكثر تأثرًا بالتغييرات غير المتزامنة في params؟
ج المكونات الأكثر تأثرًا هي generateMetadata و layout.tsx و page.tsx. إذا كان مشروعك يحتوي على عدد كبير من الصفحات التي تستخدم params.id، فستحتاج إلى فحصها واحدة تلو الأخرى أثناء الترحيل. نوصي بتشغيل npx @next/codemod@latest async-request-params للترحيل التلقائي أولاً، ثم فحص الحالات غير المغطاة يدويًا.
س هل proxy.ts و middleware.ts الأصلي متكافئان وظيفيًا؟
ج proxy.ts يستخدم تكوينًا تصريحيًا (يركز على "ما القواعد")، بينما middleware.ts يستخدم دوال أمرية (تركز على "كيفية التنفيذ"). proxy.ts يغطي 90% من سيناريوهات middleware (المصادقة، إعادة التوجيه، حقن الرؤوس)، ولكن لمنطق إعادة كتابة الطلبات المعقد (مثل إعادة كتابة مسارات i18n)، ستظل بحاجة إلى استخدام middleware.ts (لا يزال مدعومًا في v16).
س ماذا أفعل بتكوين Webpack بعد التحول إلى افتراضيات Turbopack؟
ج يدعم Turbopack معظم محملات وإضافات Webpack. إذا كنت تستخدم تكوين Webpack مخصص، نوصي بـ: (1) أولاً، إزالة تكوين Webpack والبناء باستخدام افتراضيات Turbopack؛ (2) إذا حدثت أخطاء، تحقق من وجود بدائل متوافقة مع Turbopack؛ (3) في الحالات النادرة التي لا تتوافق فيها التكوينات، يمكنك تجاوزها في next.config.js باستخدام experimental.turbo.rules. يخطط فريق Vercel لتحقيق التوافق الكامل مع تكوينات Webpack في v16.3.
س ما الفرق بين تخزين use cache وتخزين fetch السابق؟
ج تخزين fetch تلقائي وضمني (افتراضي force-cache)، ولا يمكن التحكم فيه بدقة. use cache هو توجيه تخزين صريح على مستوى المكون يدعم cacheTag (انتهاء الصلاحية حسب الوسم) و cacheLife (سياسات دورة الحياة). مسار الترحيل: (1) يمكن الاحتفاظ بتخزين fetch البسيط؛ (2) السيناريوهات التي تتطلب إبطالًا عند الطلب يجب أن تنتقل إلى use cache + cacheTag؛ (3) الكود الجديد يجب أن يستخدم use cache مباشرة.
س ما الذي يجب فعله كاستجابة طارئة بعد تسرب متغير بيئة؟
ج (1) قم فورًا بتدوير جميع الأسرار في GitHub Secrets أو منصة السحابة؛ (2) افحص سجل Git بحثًا عن التسريبات (git log -p -S 'sk_live_')؛ (3) إذا كان السر قد تعرض تحت NEXT_PUBLIC_، افترض أن جميع الزوار السابقين قد يكونون قد وصلوا إليه وقم بتدويره؛ (4) أضف github-secret-scanning و .env* إلى .gitignore لمنع تسرب آخر.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (⭐): قم بإعداد رؤوس الأمان الستة (CSP، HSTS، X-Frame-Options، X-Content-Type-Options، Referrer-Policy، Permissions-Policy) في next.config.js، ثم استخدم curl -I أو Mozilla Observatory للتحقق من أن التكوين نشط.

  2. تمرين متقدم (⭐⭐): قم بترحيل مشروع يستخدم Next.js 14 إلى v16: (1) شغّل npx @next/codemod async-request-params لإجراء ترحيل تلقائي؛ (2) أصلح يدويًا أي استدعاءات متزامنة غير مغطاة لـ cookies() و headers()؛ (3) حول middleware.ts إلى proxy.ts (حماية مسار المصادقة)؛ (4) أزل تكوين webpack وتحقق من نجاح بناء Turbopack.

  3. تحدي (⭐⭐⭐): نفذ خط أنابيب CI/CD آمنًا كاملاً: (1) أضف حاجز npm audit إلى GitHub Actions (امنع الدمج بمستوى خطورة "high" أو أعلى)؛ (2) ادمج فحوصات Snyk وعلّق تلقائيًا على تقارير الثغرات في طلبات السحب؛ (3) نفذ سكريبت التحقق من رؤوس الأمان بعد البناء وابحث في نواتج البناء عن تسريبات NEXT_PUBLIC_؛ (4) شغّل تدقيق أمان كامل أسبوعيًا تلقائيًا وأنشئ تقرير HTML.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%