เช็คเวอร์ชัน PHP ทุกเว็บก่อนแพตช์ PHP2Shell — ทำไม php -v อย่างเดียวถึงไม่พอ
วิธีเช็คเวอร์ชัน PHP ที่เว็บรันจริง ทั้ง WordPress, Laravel, shared hosting และ Docker เทียบเลขแพ็กเกจกับ tracker ของ distro จดลงตารางเดียวก่อนแพตช์ PHP2Shell
เช็คเวอร์ชัน PHP ทุกเว็บก่อนแพตช์ PHP2Shell — ทำไม php -v อย่างเดียวถึงไม่พอ
วันที่ 21 ตุลาคม 2026 PHP ออกเวอร์ชัน 8.5.12, 8.4.27, 8.3.36 และ 8.2.35 เพื่อปิดช่องโหว่คู่ที่ถูกเรียกว่า PHP2Shell (รายละเอียดของช่องโหว่อ่านได้ที่นี่) คำถามแรกที่ทีมส่วนใหญ่เจอจริงๆ ไม่ใช่ "แพตช์ยังไง" แต่เป็น "ตกลงเว็บเรารัน PHP เวอร์ชันอะไรอยู่กันแน่"
คำตอบสั้น: อย่าเช็คจาก shell อย่างเดียว ให้ยืนยันจากฝั่งเว็บว่า PHP runtime ตัวไหนเสิร์ฟหน้าเว็บจริง (PHP-FPM, mod_php, LiteSpeed ฯลฯ) แล้วหาว่าแพ็กเกจไหนเป็นตัวจ่าย runtime นั้น จดเลขเวอร์ชันเต็มกับ repository ที่มา เช็คทั้งสอง CVE ใน tracker ของผู้ทำแพ็กเกจ แล้วรวมทุกเว็บไว้ในตารางเดียว
ต้องละเอียดขนาดนี้เพราะ php.net ระบุว่า PHP ทุกตัวที่เสิร์ฟ web request ได้รับผลกระทบ ทั้ง PHP-FPM, mod_php, php-cgi, FrankenPHP และ LiteSpeed ส่วน NCSC ของอังกฤษเตือนว่าอย่าสำรวจแค่เครื่องที่ใช้เวอร์ชันเก่า
ทำไม php -v ถึงตอบไม่ตรงคำถาม?
php -v ถาม PHP ตัว CLI ใน PATH ของเครื่อง ไม่ใช่ตัวที่ web server เรียกใช้ คู่มือ PHP อธิบายว่า php_sapi_name() คืนค่า cli เมื่อรันจาก command line แต่ถ้ารันผ่านเว็บจะได้ค่าอย่าง fpm-fcgi, apache2handler หรือ litespeed ซึ่งอาจเป็นคนละ binary คนละเวอร์ชันกับตัว CLI
นอกจากนี้ PHP-FPM แต่ละ pool ใช้ php.ini คนละไฟล์ได้ เอกสาร WP-CLI เขียนว่า php.ini ของ CLI "typically different than web" cPanel และ Plesk ตั้งเวอร์ชันรายโดเมนได้ และ container ก็มี PHP ของตัวเอง
เช็คจากฝั่งเว็บ: วิธีไหนเหมาะกับเว็บแบบไหน
| วิธี | ได้ข้อมูลอะไร | ข้อควรระวัง |
|---|
| WordPress: Tools → Site Health → Info → Server | PHP version (runtime) และ PHP SAPI ของ request จริง | ไม่ใช่เลขแพ็กเกจ, wp cli info บอกแค่ฝั่ง CLI |
ไล่ fastcgi_pass ใน vhost ไปจนถึง FPM ตัวที่รับงาน | FPM ที่ config ไว้ให้เว็บนี้ | ต้องยืนยันด้วย request จริง binary บนดิสก์อาจต่างจาก worker ที่รันอยู่ |
| หน้า diagnostic ที่ป้องกันไว้ หรือ route ที่จำกัดสิทธิ์ | PHP_VERSION และ PHP_SAPI ผ่านเส้นทาง request จริง | ห้ามเปิด phpinfo() สู่สาธารณะ |
docker exec <container> php -v | binary ตัว CLI ใน container | ไม่ใช่หลักฐานว่าตัวไหนเสิร์ฟ request |
WordPress ง่ายที่สุด หัวข้อ Server ใน Site Health แสดง "PHP version" กับ "PHP SAPI" ที่อ่านระหว่าง request นั้น จึงเป็น runtime ที่เสิร์ฟเว็บจริง (คัดลอกข้อมูลเก็บไว้ได้) แต่เป็นเวอร์ชัน runtime ไม่ใช่เลขแพ็กเกจ
Laravel หรือแอปที่เขียนเองบน nginx บรรทัด fastcgi_pass บอกปลายทาง FastCGI ที่ config ไว้ ซึ่ง nginx ให้เป็นได้ทั้ง UNIX socket (เอกสาร Laravel ใช้ unix:/var/run/php/php8.2-fpm.sock), host กับ port หรือ server group ให้ไล่ไปถึง FPM ที่ฟังอยู่ปลายทางนั้น รัน binary ตัวนั้นด้วย -v แล้วยืนยันด้วย request จริง เพราะ binary บนดิสก์อาจต่างจาก worker ที่รันอยู่
Apache ที่ใช้ mod_php บรรทัด LoadModule php_module เป็นชื่อของ PHP 8 (PHP 7 ใช้ php7_module) ให้เช็ค config ที่มีผลจริงรวมไฟล์ที่ include เข้ามา (httpd -M แสดง module ที่โหลดอยู่) แล้วยืนยัน SAPI จากฝั่งเว็บ เว็บกลุ่มนี้ควรอยู่บนสุดของรายการ เพราะ php.net ระบุว่า mod_php คือกรณีเสี่ยงที่สุดของ PHP2Shell
ไฟล์เช็คบรรทัดเดียวใช้ได้เฉพาะเมื่อ server รันไฟล์นั้นผ่าน PHP handler ที่ตั้งใจไว้ แอปแบบ front controller อย่าง Laravel ส่งทุก request ผ่าน public/index.php ไฟล์ที่วางเพิ่มจึงอาจไม่ถูกรันเลย ทางที่ดีกว่าคือใช้หน้า diagnostic ที่ป้องกันไว้แล้ว หรือ route ที่จำกัดสิทธิ์ซึ่งพิมพ์แค่นี้:
<?php echo PHP_VERSION, ' ', PHP_SAPI;
PHP_VERSION มีค่าเดียวกับ phpversion() และ PHP_SAPI เท่ากับ php_sapi_name() ตามคู่มือ PHP ส่วน phpinfo() ห้ามเปิดเป็น endpoint สาธารณะเด็ดขาด แม้แค่ชั่วคราว คู่มือ PHP ระบุว่าผลลัพธ์รวม environment variables และข้อมูล EGPCS ไว้ด้วย รหัสผ่านฐานข้อมูลใน environment variable จึงอาจโผล่ได้ ส่วน X-Powered-By เป็นแค่เบาะแส ค่าเริ่มต้นของ expose_php ใส่เลขเวอร์ชันไว้ แต่ปิดได้ และ config ตัวอย่างของ Laravel ก็ซ่อนไว้แล้ว
Shared hosting ดูเวอร์ชันได้จาก control panel (ใน cPanel ค่า "inherit" คือ default ที่ผู้ดูแลเซิร์ฟเวอร์กำหนด) ส่วนการแพตช์ตัว PHP เป็นงานของผู้ให้บริการ ขอเลขแพ็กเกจและสถานะแพตช์จากเขาเป็นลายลักษณ์อักษร
Docker คำสั่ง docker exec <container> php -v เช็คแค่ binary ตัว CLI ใน container และ ENV PHP_VERSION เป็นแค่ metadata ของ image ทั้งคู่ไม่ได้พิสูจน์ว่า worker ตัวไหนตอบ request ต้องยืนยันผ่านเส้นทาง request จริง เอกสาร Docker ระบุว่า tag ถูกเปลี่ยนให้ชี้ image ใหม่ได้ และ image ที่ build แล้วคือ snapshot การ rebuild ด้วย --pull จึงไม่เปลี่ยน container ที่รันอยู่ ต้อง build จาก base ที่ยืนยันแล้วว่าแก้แล้ว แทนที่ container ผ่านการ deploy ปกติ แล้วเช็คซ้ำทุก replica ส่วน base ที่ pin ไว้กับเวอร์ชันที่มีช่องโหว่ก็ยังมีช่องโหว่
เลขเวอร์ชันดูเก่า แต่แพตช์แล้ว เป็นไปได้ยังไง?
distribution มัก backport fix เข้าเวอร์ชันที่มากับ release (ตาม Security FAQ ของ Debian) และ Red Hat เตือนว่าดูแค่เลขเวอร์ชันบอกไม่ได้ว่าเสี่ยงหรือไม่ ตัวอย่าง: แพ็กเกจ php8.3 ของ Ubuntu 24.04 มีเลข upstream เป็น 8.3.6 (เลขแพ็กเกจคือ 8.3.6-0ubuntu0.24.04.x) ต่ำกว่า 8.3.36 อยู่มาก ถ้าดูแค่เลข runtime เครื่องนี้จะถูกตีว่าไม่ได้แพตช์ตลอดไป ส่วน php8.1 บน Ubuntu 22.04 (8.1.2-1ubuntu2.x) ยังได้อัปเดตอยู่ (Launchpad แสดงอัปเดตวันที่ 21 ตุลาคม 2026) ทั้งที่ php.net หยุดซัพพอร์ต 8.1 ไปตั้งแต่ 31 ธันวาคม 2025
Site Health หรือหน้า diagnostic บอกเวอร์ชัน runtime ไม่ใช่เลขแพ็กเกจ จึงต้องเช็คแพ็กเกจที่อยู่เบื้องหลัง runtime นั้น (ทุกคำสั่งอ่านอย่างเดียว):
- หาว่าแพ็กเกจไหนเป็นเจ้าของ binary:
dpkg -S <path> บน Debian/Ubuntu หรือ rpm -qf <path> บน RHEL และ rebuild อย่าง AlmaLinux
- จดเลขเวอร์ชันเต็มกับ repository:
apt-cache policy <ชื่อแพ็กเกจ> บอกว่ามาจาก source ไหน ส่วน apt-get changelog <ชื่อแพ็กเกจ> หรือ rpm -q --changelog <ชื่อแพ็กเกจ> แสดงประวัติ
- เช็คทั้งสอง CVE ใน tracker ของผู้ทำแพ็กเกจ เช่น Ubuntu CVE-2026-109618 หรือ Debian tracker CVE-2026-109618 (และหน้า CVE-2026-109619) เทียบกับบรรทัด fixed ของ release ที่ใช้อยู่ ณ ตอนนั้น ทีละแพ็กเกจ
ถ้าท้ายเลขไม่ใช่รูปแบบของ distro (เช่นตัวอย่างในเอกสาร WP-CLI ที่ลงท้าย +deb.sury.org+1) แพ็กเกจมาจาก repository อื่น ต้องใช้หลักฐานของ repository นั้น ส่วน image ทางการของ PHP บน Docker คอมไพล์จาก source ของ php.net ให้เทียบกับเวอร์ชันที่ PHP ออก fix ไม่ใช่ tracker ของ distro (Sansec รายงานว่า image นี้ไม่มี fix ให้ 8.1 หรือเก่ากว่า ซึ่งเป็น branch ที่ php.net ไม่อัปเดตแล้ว)
ตารางอินเวนทอรีที่ใช้ได้จริง
ทั้งหมดนี้ต้องเริ่มจากรายชื่อเว็บที่ครบ ที่หลุดบ่อยคือ microsite เก่า หรือ appliance ที่มี PHP ฝังอยู่ (ศูนย์ไซเบอร์ของแคนาดาและ NCSC เตือนให้นับด้วย) ถ้ารายชื่อกระจายอยู่หลายที่ EASM และ CAASM ของ zcrCTEM ช่วยตั้งต้นรายการนี้ได้ EASM ค้นหา host ที่เปิดสู่อินเทอร์เน็ตโดยเริ่มจาก root domain ขององค์กร CAASM รวม host, device และ cloud resource จากหลาย source เป็น inventory ชุดเดียว และ Sync Health เตือนเมื่อ source ไหนเงียบไป ส่วนเวอร์ชัน PHP ของแต่ละเครื่องยังต้องเช็คด้วยวิธีด้านบน
ตัวอย่างตาราง (ข้อมูลสมมติ):
| เว็บไซต์ | เครื่อง/โฮสต์ | SAPI | runtime / แพ็กเกจ (repo) | ได้เลขมาจาก | แพตช์แล้ว? | เจ้าของ |
|---|
| www.example.com | shared hosting (cPanel) | litespeed | ไม่ทราบ / ถามผู้ให้บริการ | MultiPHP Manager + หน้าเช็คที่ป้องกันไว้ | ไม่ทราบ รอตรวจยืนยัน | การตลาด |
| shop.example.com | VPS Ubuntu 24.04 | fpm-fcgi | 8.3.6 / 8.3.6-0ubuntu0.24.04.x (Ubuntu archive) | Site Health + dpkg -S, apt-cache policy | ไม่ทราบ รอตรวจยืนยัน | ทีมเว็บ |
| api.example.com | container php:8.3-fpm | ยังไม่ได้เช็ค | เช็คแค่ CLI | docker exec | ไม่ทราบ รอตรวจยืนยัน | ทีม dev |
| intranet | เซิร์ฟเวอร์เก่า Apache | apache2handler | 7.4.x / ไม่ทราบ | หน้าเช็คที่ป้องกันไว้ | ไม่ทราบ รอตรวจยืนยัน (7.4 หมดซัพพอร์ตจาก php.net แล้ว) | IT |
| ระบบสำเร็จรูปของ vendor | appliance | ไม่ทราบ | ไม่ทราบ | ถาม vendor | ไม่ทราบ | จัดซื้อ |
จะใส่ว่า "แพตช์แล้ว" ได้ต้องมีครบสามอย่าง: เลข build ที่ติดตั้งจริงแบบเต็ม, หลักฐานจาก tracker ว่าแก้ทั้งสอง CVE และการยืนยันว่า worker ตัวที่อัปเดตแล้วกำลังเสิร์ฟ request อยู่
ลำดับการทำงาน:
- รวมรายชื่อให้ครบก่อน ทั้ง DNS, บัญชีโฮสติ้ง, cloud, container และ appliance
- เช็คเวอร์ชัน runtime จากฝั่งเว็บทีละเว็บ
- จดเลข build ของแพ็กเกจแบบเต็มพร้อม repository แล้วเช็คทั้งสอง CVE
- เรียงความเร่งด่วน เว็บที่เปิดสู่อินเทอร์เน็ตมาก่อน และในกลุ่มนั้นให้ mod_php มาก่อน
- หลังแพตช์ให้ restart หรือแทนที่ PHP worker ทุกตัว แล้วเช็คซ้ำจากฝั่งเว็บ (เว็บบน 8.2 ควรวางแผนย้าย branch ด้วย เพราะ 8.2 หมดซัพพอร์ตวันที่ 31 ธันวาคม 2026)
ถ้ารายการยาวเกินกว่าจะไล่ด้วย spreadsheet ไหว ดูแพ็กเกจและราคา zcrCTEM หรือลองเดโมก่อนตัดสินใจได้
แพตช์ไม่ได้ทันที ทำอะไรได้บ้าง?
PHP บอกว่า workaround ช่วยลดความเสี่ยงได้แต่ไม่ได้ทำให้ปลอดภัย อ่านต่อได้ที่ WAF ช่วยได้แค่ไหนถ้ายังแพตช์ไม่ได้ และถ้าตารางมีเครื่องที่ยังไม่ได้แพตช์ ให้ไล่ต่อด้วยวิธีหาร่องรอยการโจมตีจาก log
ข้อจำกัดที่ควรรู้
- ทุกวิธีข้างบนบอก เวอร์ชัน ไม่ได้บอกว่าเครื่องถูกเจาะไปแล้วหรือยัง
- การเช็คจากฝั่งเว็บบอกเฉพาะ pool ที่เสิร์ฟ request นั้น ถ้าเว็บเดียวแบ่ง path ไปหลาย pool ต้องเช็คทุก pool
- สถานะใน tracker เปลี่ยนได้ เปิดดูตอนตัดสินใจจริง อย่าจำจากครั้งก่อน
เครื่องมือฟรีก็ทำงานนี้ได้ดี
ถ้ามีเว็บไม่กี่ตัว Site Health, คำสั่ง dpkg/apt หรือ rpm, หน้า tracker และ spreadsheet หนึ่งไฟล์ก็พอ ไม่ต้องซื้ออะไรเพิ่ม เครื่องมือแบบแพลตฟอร์มจะคุ้มเมื่อ "รายชื่อเว็บ" เองคือสิ่งที่องค์กรยังตอบไม่ได้
📞 ปรึกษาฟรี
บทความที่เกี่ยวข้อง
อ่านต่อเพื่อวางแผนเรื่อง inventory และพื้นผิวที่เปิดสู่ภายนอก:
ผลิตภัณฑ์ที่เกี่ยวข้อง
อ่านรายละเอียด zcrCTEM เพื่อดูขอบเขตของ EASM และ CAASM ดูราคาที่เผยแพร่ได้ที่หน้าราคา
แหล่งข้อมูลอ้างอิง
- PHP News Archive 2026 — เวอร์ชันที่แก้แล้ว, SAPI ที่ได้รับผลกระทบ, mod_php เสี่ยงที่สุด, สถานะ PHP 8.1 และเก่ากว่า, วันหมดอายุของ 8.2
- PHP Manual — php_sapi_name() — ค่า
cli, fpm-fcgi, apache2handler, litespeed และ PHP_SAPI
- PHP Manual — phpversion() — ความสัมพันธ์กับ
PHP_VERSION
- PHP Manual — phpinfo() — ข้อมูล EGPCS และ environment ที่แสดงออกมา
- PHP Manual — FastCGI Process Manager (FPM) — pool ที่ใช้ php.ini ต่างกัน
- PHP Manual — Apache 2.x on Unix —
LoadModule php_module; php-src UPGRADING 8.0 — เปลี่ยนชื่อจาก php7_module; Apache httpd(8) — -M
- PHP Manual — php.ini core directives (expose_php)
- php-fpm man page (php-src) — ตัวเลือก
-v; nginx — fastcgi_pass — socket, address หรือ server group
- PHP — Unsupported Branches — วันหมดอายุของ 8.1
- WordPress — Site Health screen และ WP_Debug_Data::get_wp_server()
- WP-CLI — wp cli info
- Laravel 12.x — Deployment — front controller
public/index.php, ตัวอย่าง fastcgi_pass และ fastcgi_hide_header X-Powered-By
- cPanel — MultiPHP Manager และ Plesk — PHP Settings
- Docker — Building best practices, docker container exec และ Dockerfile ของ image ทางการ
- Debian Security FAQ, Red Hat — Backporting Security Fixes และ Ubuntu — About CVE statuses
- Launchpad — php8.1 ใน Ubuntu และ Launchpad — php8.3 ใน Ubuntu
- dpkg(1), apt-cache(8), apt-get(8) และ rpm(8)
- Sansec — PHP2Shell, UK NCSC และ Canadian Centre for Cyber Security AL26-026