آپدیت امنیتی PHP بدون خراب شدن سایت: تجربه‌ی ما از وصله‌ی بحرانی اکتبر ۲۰۲۶

در ۲۱ اکتبر ۲۰۲۶، تیم PHP نسخه‌های 8.5.12، 8.4.27، 8.3.36 و 8.2.35 را منتشر کرد. این نسخه‌ها دو آسیب‌پذیری CVE-2026-109618 و CVE-2026-109619 را برطرف می‌کنند که با امتیاز CVSS v4 برابر 9.2 در رده‌ی بحرانی قرار گرفته‌اند و به مهاجم اجازه‌ی اجرای کد از راه دور بدون احراز هویت می‌دهند. شرکت امنیتی watchTowr گزارش داده که این آسیب‌پذیری در دنیای واقعی مورد سوءاستفاده قرار گرفته است، و طبق اعلام PHP، هیچ تغییر تنظیماتی به‌طور کامل جلوی آن را نمی‌گیرد. یعنی تنها راه‌حل واقعی، نصب وصله است.

ما این آپدیت را روی سرور Plesk خودمان انجام دادیم. در این مقاله هم آن تجربه را تعریف می‌کنیم و هم آن را به یک روش عملی تبدیل می‌کنیم که روی سرور شما هم قابل اجرا باشد.

نسخه‌های قدیمی‌تر چه می‌شوند؟

شاخه‌های 8.1 و قدیمی‌تر دیگر از طرف PHP پشتیبانی نمی‌شوند و نسخه‌ی رسمی جدیدی برای آن‌ها منتشر نشد. با این حال، وصله‌های حداقلی در سطح کد منبع منتشر شد و توزیع‌های لینوکس و فروشندگان پشتیبانی طولانی‌مدت، نسخه‌های وصله‌شده‌ی خودشان را عرضه کردند. برای نمونه:

  • Plesk بیلدهای وصله‌شده‌ی 7.4.33 و 8.1.34 را با تاریخ ۲۱ اکتبر منتشر کرد. شماره‌ی نسخه همان قبلی است؛ آن‌چه تغییر کرده، تاریخ بیلد است.
  • Ubuntu 24.04 بسته‌ی php8.3 نسخه‌ی 8.3.6-0ubuntu0.24.04.13 را منتشر کرد که وصله در Changelog آن ثبت شده است.

همین نکته مهم‌ترین دام این آپدیت است: شماره‌ی نسخه‌ی PHP به‌تنهایی نمی‌گوید وصله نصب شده یا نه. روی Ubuntu نسخه همچنان 8.3.6 نشان داده می‌شود، چون توزیع‌ها وصله‌ها را Backport می‌کنند.

قدم ۱: همه‌ی نسخه‌های PHP نصب‌شده را پیدا کنید

روی سرورهای دارای کنترل‌پنل، تقریباً همیشه چند نسخه‌ی PHP کنار هم نصب است و php -v فقط یکی از آن‌ها را نشان می‌دهد. هر کدام باید جداگانه بررسی شود:

# Plesk
for p in /opt/plesk/php/*/bin/php; do echo "== $p"; $p -v | head -1; done

# cPanel (EasyApache)
for p in /opt/cpanel/ea-php*/root/usr/bin/php; do echo "== $p"; $p -v | head -1; done

# DirectAdmin
ls -d /usr/local/php*/bin/php 2>/dev/null; /usr/local/bin/php -v | head -1

# بسته‌های خود Ubuntu
ls /usr/bin/php[0-9]* 2>/dev/null
dpkg -l | grep -E '^ii\s+php[0-9.]+-(cli|fpm)'

خط اول خروجی php -v شامل تاریخ بیلد است، مثلاً (built: Oct 21 2026 ...). ما روی سرور خودمان برای هر بیلد PHP همین تاریخ را بررسی کردیم. فراموش نکنید PHP ممکن است بیرون از کنترل‌پنل هم نصب باشد؛ مثلاً داخل کانتینرهای Docker، که با docker exec باید جداگانه بررسی شوند و با بازسازی ایمیج آپدیت می‌شوند، نه با apt.

قدم ۲: وصله نصب شده است یا نه؟

منبع PHP چطور بررسی کنیم
نسخه‌ی رسمی (upstream)، مثلاً بیلد از کد منبع یا مخازنی که نسخه‌ی upstream را دنبال می‌کنند شماره‌ی نسخه باید حداقل 8.5.12، 8.4.27، 8.3.36 یا 8.2.35 باشد
بسته‌های توزیع (Ubuntu، Debian، RHEL و…) Changelog بسته باید شناسه‌ی CVE را داشته باشد
بیلدهای کنترل‌پنل برای نسخه‌های قدیمی (مثل 7.4 و 8.1 در Plesk) تاریخ بیلد و یادداشت انتشار پنل

برای بسته‌های Ubuntu، Changelog را مستقیم جست‌وجو کنید:

zcat /usr/share/doc/php8.3-cli/changelog.Debian.gz | grep -n -m3 -E 'CVE-2026-10961[89]'
dpkg -l php8.3-cli | tail -1

اگر شناسه‌ی CVE پیدا نشد، بسته هنوز آپدیت نشده است. اطلاعیه‌های امنیتی Ubuntu (USN) هم فهرست دقیق بسته‌ها و نسخه‌های وصله‌شده را دارند.

قدم ۳: بسته‌ی جدید نصب شده؛ آیا واقعاً محافظت می‌شوید؟

این بخشی است که خیلی‌ها جا می‌اندازند. نصب بسته‌ی جدید، فایل اجرایی روی دیسک را عوض می‌کند، اما پروسه‌های PHP-FPM که قبلاً شروع شده‌اند، هنوز کد قدیمی و آسیب‌پذیر را در حافظه دارند تا وقتی دوباره راه‌اندازی شوند. ما روی سرور خودمان زمان شروع پروسه‌های master در PHP-FPM را با زمان نصب وصله مقایسه کردیم:

ps -eo pid,lstart,cmd | grep '[p]hp-fpm: master'

هر master که قبل از نصب وصله شروع شده باشد، باید ری‌استارت شود. یک نکته‌ی ظریف: reload در PHP-FPM (سیگنال USR2) پروسه‌ی master را با همان PID از نو اجرا می‌کند، پس ممکن است زمان شروع در خروجی بالا تغییر نکند. معیار دقیق‌تر این است که فایل اجرایی پروسه به فایل حذف‌شده اشاره نکند:

for pid in $(pgrep -f 'php-fpm: master'); do
  echo "$pid $(readlink /proc/$pid/exe)"
done
# اگر جلوی مسیر عبارت (deleted) دیدید، پروسه هنوز بیلد قدیمی را اجرا می‌کند

ابزار needrestart که روی Ubuntu Server معمولاً نصب است هم سرویس‌هایی را که با کتابخانه یا فایل اجرایی قدیمی کار می‌کنند فهرست می‌کند: needrestart -r l.

در Plesk، اگر سایتی به‌جای FPM با FastCGI اجرا شود، هر درخواست پروسه‌ی جدید می‌سازد یا پروسه‌ها بعد از مدتی بازسازی می‌شوند؛ با این حال برای اطمینان، بعد از آپدیت فهرست پروسه‌های PHP را بررسی کنید.

قدم ۴: روش آپدیت امن

  1. بکاپ: از سایت‌ها و دیتابیس‌ها بکاپ تازه داشته باشید و مطمئن باشید خارج از سرور ذخیره شده است. روی سرور مجازی، Snapshot هم گزینه‌ی خوبی است.
  2. ثبت وضعیت فعلی سایت‌ها: قبل از آپدیت، کد وضعیت و زمان پاسخ صفحه‌های اصلی را ثبت کنید:
    for u in https://example.com/ https://shop.example.com/ https://example.com/wp-login.php; do
      curl -s -o /dev/null -m 20 -w "%{http_code} %{time_total}s $u\n" "$u"
    done | tee /root/before.txt
  3. شبیه‌سازی آپدیت: قبل از آپدیت واقعی، ببینید apt دقیقاً چه کاری می‌خواهد انجام دهد:
    apt-get update
    apt-get -s upgrade | grep -E '^(Inst|Remv)'
    apt-get -s dist-upgrade | grep -E '^Remv'

    هر خط Remv یعنی بسته‌ای حذف خواهد شد. این خطوط را جدی بگیرید.

  4. آپدیت: در Plesk از بخش Updates یا با plesk installer، و برای بسته‌های سیستم با apt-get upgrade. ترجیحاً در ساعات کم‌ترافیک.
  5. راه‌اندازی دوباره‌ی FPM: برای هر نسخه، مثلاً systemctl reload php8.3-fpm یا در Plesk systemctl reload plesk-php83-fpm. reload درخواست‌های در حال اجرا را قطع نمی‌کند. سپس قدم ۳ را تکرار کنید.
  6. مقایسه‌ی قبل و بعد: همان حلقه‌ی curl را با فایل after.txt اجرا کنید و با diff مقایسه کنید. علاوه بر کد 200، لاگ خطای PHP و وب‌سرور را هم چند دقیقه نگاه کنید.

آن‌چه در شبیه‌سازی دیدیم

روی سرور ما، شبیه‌سازی با apt-get -s نشان داد که آپدیت یکی از بسته‌های PHP داخلی، که سایتی را سرو نمی‌کرد و فقط توسط یک ابزار امنیتی استفاده می‌شد، یک بسته‌ی وابسته را حذف می‌کند. به‌جای اینکه ریسک از کار افتادن آن ابزار را بپذیریم، آپدیت همان یک بسته را به تعویق انداختیم تا جداگانه بررسی شود؛ چون آن PHP در معرض درخواست‌های اینترنت نبود و همه‌ی نسخه‌هایی که سایت‌ها را اجرا می‌کردند وصله شده بودند. این تصمیم فقط با شناخت دقیق نقش هر بسته ممکن است. اگر PHP مورد نظر سایتی را اجرا می‌کرد، تعویق گزینه نبود.

قدم ۵: سایت‌هایی که هنوز روی PHP 7.4 یا 8.1 هستند

این بار توزیع‌ها و کنترل‌پنل‌ها برای نسخه‌های قدیمی وصله منتشر کردند، اما هیچ تضمینی نیست که دفعه‌ی بعد هم این اتفاق بیفتد. PHP 7.4 از سال ۲۰۲۲ و 8.1 از پایان سال ۲۰۲۵ پشتیبانی رسمی ندارند، و پشتیبانی امنیتی 8.2 هم تا پایان ۲۰۲۶ ادامه دارد. سایتی که روی نسخه‌ی خارج از پشتیبانی مانده، هر بار به لطف دیگران امن می‌ماند. مسیر پیشنهادی ارتقا:

  1. فهرست سایت‌ها و نسخه‌ی PHP هر کدام را تهیه کنید.
  2. بررسی خودکار سازگاری کد: ابزار PHP_CodeSniffer با استاندارد PHPCompatibility بسیاری از ناسازگاری‌ها را بدون اجرای سایت پیدا می‌کند. استاندارد و نسخه‌ی هدف را طبق مستندات خود ابزار تنظیم کنید؛ مثلاً:
    phpcs -p --standard=PHPCompatibility --runtime-set testVersion 8.3 --extensions=php /path/to/site
  3. کپی Staging: سایت را روی یک زیردامنه یا سرور آزمایشی با نسخه‌ی جدید بالا بیاورید و مسیرهای اصلی (ورود، فرم‌ها، پرداخت، پنل مدیریت) را تست کنید. در وردپرس، افزونه‌ها و قالب‌های قدیمی و در لاراول، نسخه‌ی خود فریم‌ورک و پکیج‌های Composer معمولاً منبع اصلی مشکل‌اند.
  4. ارتقای مرحله‌ای: از 7.4 مستقیم به آخرین نسخه نپرید اگر کد قدیمی است؛ گاهی 7.4 به 8.1 و بعد 8.3 کم‌دردسرتر است. در کنترل‌پنل‌ها می‌توانید نسخه‌ی PHP را برای هر سایت جداگانه تغییر دهید و در صورت مشکل برگردانید.
  5. بعد از ارتقا چند روز لاگ خطای PHP را برای هشدارهای Deprecated و خطاهای جدید زیر نظر بگیرید.

جمع‌بندی

  • همه‌ی نسخه‌های PHP سرور را پیدا کنید، نه فقط آن که php -v نشان می‌دهد.
  • برای بسته‌های توزیع، به Changelog نگاه کنید، نه شماره‌ی نسخه.
  • تا وقتی PHP-FPM دوباره راه‌اندازی نشده، وصله اثری ندارد.
  • قبل از آپدیت شبیه‌سازی کنید و قبل و بعد سایت‌ها را تست کنید.
  • نسخه‌های خارج از پشتیبانی را برنامه‌ریزی‌شده ارتقا دهید، نه در روز بحران بعدی.

اگر احتمال می‌دهید سایتی قبل از نصب وصله مورد نفوذ قرار گرفته، آپدیت کافی نیست؛ راهنمای بازیابی سایت هک‌شده و چک‌لیست سلامت سرور لینوکس را ببینید.

نمی‌خواهید این کار را خودتان انجام دهید؟ تیم مروانت آپدیت‌های امنیتی PHP، وب‌سرور و سیستم‌عامل را روی سرورهای Plesk، cPanel، DirectAdmin و سرورهای بدون پنل با همین روش، یعنی بکاپ، شبیه‌سازی و تست قبل و بعد، انجام می‌دهد. صفحه‌ی مدیریت و پشتیبانی سرور را ببینید.

دیدگاهتان را بنویسید

جستجو

فهرست مطالب