ทีมความปลอดภัยของ PHP ทำในสิ่งที่ไม่ค่อยได้ทำ: เตือนเรื่องแพตช์ไว้ล่วงหน้า วันที่ 20 ตุลาคม มีข้อความขึ้นในเมลลิสต์ php.announce และหน้าแรกของ php.net ว่าวันที่ 21 ตุลาคม เวลา 14:00 UTC จะมีอัปเดตนอกรอบที่ปิดช่องโหว่ร้ายแรงสองตัว ทั้งคู่ได้คะแนน 9.2 ตาม CVSS เวอร์ชัน 4.0 ในประกาศไม่มีทั้งคำอธิบาย หมายเลข CVE และรายการเวอร์ชันที่ได้รับผลกระทบ: ทุกอย่างจะเผยแพร่พร้อมแพตช์ตามที่สัญญาไว้
แพตช์ออกมาเร็วกว่าที่สัญญาไว้ด้วยซ้ำ: รายละเอียดของช่องโหว่ขึ้นเวลา 13:34 UTC ก่อนกำหนดยี่สิบกว่านาที
คำตอบสั้น ๆ ถ้าต้องการแค่นี้: อัปเดตเป็น 8.2.35, 8.3.36, 8.4.27 หรือ 8.5.12 ตามสายที่คุณใช้ ช่องโหว่ทั้งสองตัวคือการรันโค้ดจากระยะไกลโดยไม่ต้องล็อกอิน และไม่ขึ้นกับว่าใช้ CMS อะไร ต่อจากนี้คือรายละเอียด และสิ่งที่ต้องทำถ้า “แค่อัปเดต” ใช้ไม่ได้ในกรณีของคุณ
ถ้าไม่มีเวลาอ่าน นี่คือทางแยก เว็บไซต์เป็นแบบสแตติก ไม่มีโค้ดฝั่งเซิร์ฟเวอร์: เรื่องนี้ไม่เกี่ยวกับคุณ เว็บไซต์อยู่บนโฮสติ้งทั่วไป: ผู้ให้บริการโฮสติ้งจะเป็นคนอัปเดต หน้าที่ของคุณคือเช็กเวอร์ชันและคอยตามเรื่อง มีเซิร์ฟเวอร์ของตัวเองหรือใช้คอนเทนเนอร์: คุณต้องอัปเดตเอง และด้านล่างมีคำสั่งให้ ถ้าไม่รู้ว่าตัวเองเป็นกรณีไหนในสามกรณีนี้ ให้เริ่มจากขั้นตอนแรก
PHP คืออะไร และทำไมเรื่องนี้ถึงเกี่ยวกับคุณ
บทนำสั้น ๆ สำหรับคนที่เคยได้ยินชื่อ PHP แต่ไม่เคยใช้ PHP เป็นภาษาที่ CMS ยอดนิยมหลายตัวใช้ ทั้ง WordPress, Joomla, Drupal, Bitrix, OpenCart และเว็บไซต์ที่เขียนขึ้นเองของบริษัทเล็ก ๆ ส่วนใหญ่ มันไม่ได้ทำงานในเบราว์เซอร์ แต่ทำงานบนเซิร์ฟเวอร์: เมื่อมีคนเปิดหน้าเว็บ เซิร์ฟเวอร์จะรันโค้ด PHP แล้วส่ง HTML ที่เสร็จแล้วกลับไป
จากตรงนี้ได้ข้อสรุปสองข้อ ข้อแรก: ช่องโหว่ในตัว PHP เองไม่ใช่ช่องโหว่ในปลั๊กอินตัวเดียวหรือธีมเดียว แต่อยู่ในรากฐานที่ทั้งเว็บไซต์ตั้งอยู่ ข้อสอง: ถ้าเว็บไซต์แค่ส่งไฟล์สำเร็จรูป เช่น เว็บสแตติกที่ทำด้วย Astro, Hugo, Tilda, Webflow หรือแลนดิ้งเพจ HTML ธรรมดา ระบบแบบนี้ไม่มี PHP อยู่เลย แต่หน้าร้านอาจมีบริการข้างเคียงหรือหน้าแอดมินแยกที่เขียนด้วย PHP ถ้าไม่แน่ใจ ถามนักพัฒนาหรือผู้ให้บริการโฮสติ้งจะดีกว่า
ทำไมถึงเตือนเรื่องแพตช์ล่วงหน้า
ปกติช่องโหว่กับแพตช์จะเผยแพร่พร้อมกัน: ตราบใดที่ช่องโหว่ยังไม่ถูกปิด รายละเอียดใด ๆ เกี่ยวกับมันก็เป็นของขวัญให้ผู้โจมตี การแจ้งล่วงหน้าจึงเป็นข้อยกเว้น
ดูจากถ้อยคำในประกาศ ตรรกะคือให้เวลาผู้ดูแลระบบเตรียมตัว ถ้าอัปเดตออกมาแบบไม่ทันตั้งตัว โครงสร้างพื้นฐานขนาดใหญ่ต้องใช้เวลาเป็นวันแค่กับงานด้านการจัดการ ทั้งตกลงช่วงเวลา แจ้งลูกค้า และ build อิมเมจ ถ้าเตือนก่อนหนึ่งวัน พอถึงเวลารีลีสก็มีแผนพร้อมแล้ว ราคาที่ต้องจ่ายคือทั้งสองฝ่ายได้อ่านคำเตือนนี้เหมือนกัน
ในประกาศระบุไว้แยกว่า ถ้ารายละเอียดหลุดออกสู่สาธารณะหรือเริ่มเห็นสัญญาณการโจมตีจริง จะออกรีลีสเร็วกว่าเวลาที่กำหนด โดยเนื้อแท้แล้วนี่คือประกันความเสี่ยงจากคำเตือนของตัวเอง
คะแนน 9.2 หมายถึงอะไร และทำไมถึงถือว่าสูง
CVSS คือมาตรวัดความร้ายแรงทางเทคนิคที่ใช้กันทั้งอุตสาหกรรม มีตั้งแต่ 0 ถึง 10 ช่วง 9.0–10.0 เรียกว่าระดับวิกฤต (critical) และ 9.2 ก็อยู่ในช่วงนี้
แต่ต้องเข้าใจด้วยว่าตัวเลขนี้ไม่ได้บอกอะไร มันไม่ได้บอกว่าช่องโหว่ถูกใช้โจมตีด้วยวิธีไหน เวอร์ชันไหนโดนบ้าง ต้องตั้งค่าแบบพิเศษไหม และโมดูลที่มีช่องโหว่เปิดอยู่ในระบบของคุณหรือเปล่า บางครั้งช่องโหว่ที่คะแนนระดับวิกฤตอยู่ในส่วนขยายที่เซิร์ฟเวอร์ส่วนใหญ่ไม่มีเลย และมันก็ไม่ได้บอกความน่าจะเป็นที่เว็บไซต์ของคุณจะโดนเจาะ ถ้าไม่มีคำอธิบาย 9.2 เป็นเหตุผลให้ใส่ใจอย่างจริงจัง ไม่ใช่เหตุผลให้คิดว่าคุณโดนแฮกไปแล้ว ในกรณีนี้คำอธิบายออกมาพร้อมแพตช์ และเป็นข่าวไม่ดี ซึ่งจะเล่าในหัวข้อถัดไป
รายละเอียดข้อที่สอง: นี่เป็นคะแนนตามเวอร์ชัน 4.0 ซึ่งเป็นฉบับค่อนข้างใหม่ของวิธีประเมิน ถ้าใช้เวอร์ชันสาม ช่องโหว่เดียวกันอาจได้ตัวเลขต่างออกไป ฉะนั้นถ้าแหล่งข่าวต่าง ๆ บอกตัวเลขไม่ตรงกัน ควรเทียบทั้งตัวเลขและวิธีประเมิน
ช่องโหว่อะไรบ้าง: CVE-2026-109618 และ CVE-2026-109619
หมายเลขที่ลือกันในเน็ตตั้งแต่ก่อนรีลีสกลายเป็นของจริง: คือ CVE-2026-109618 และ CVE-2026-109619 ทั้งคู่ถูกระบุว่าเป็นระดับวิกฤต เวอร์ชันที่แก้แล้วแยกตามสาย:
| สาย PHP | เวอร์ชันที่มีช่องโหว่ | อัปเดตเป็น |
|---|---|---|
| 8.2 | ต่ำกว่า 8.2.35 | 8.2.35 |
| 8.3 | ต่ำกว่า 8.3.36 | 8.3.36 |
| 8.4 | ต่ำกว่า 8.4.27 | 8.4.27 |
| 8.5 | ต่ำกว่า 8.5.12 | 8.5.12 |
| 7.4, 8.0, 8.1 | ทุก build ที่ไม่มีแพตช์ | แพ็กเกจจากผู้ให้บริการแพ็กเกจ หรือ backport ในรูปซอร์สโค้ดจากทีม PHP |
| 7.3 และเก่ากว่า | ไม่ได้ระบุในรายละเอียด | ผู้ให้บริการแพ็กเกจ หรือย้ายไปสายที่ยังซัพพอร์ต |
ทั้งสองตัวคือการรันโค้ดจากระยะไกลโดยไม่ต้องล็อกอิน ด้วยสิทธิ์ของบัญชีผู้ใช้ที่ใช้รันเว็บเซิร์ฟเวอร์ และทั้งสองตัวเป็นเรื่องเดียวกัน: หน่วยความจำที่อยู่รอดข้ามขอบเขตของ request
ตัวแรก “RCE via OOM corruption” เป็นเรื่องหน่วยความจำหมด เมื่อ request ชนลิมิต PHP จะออกจากบริบทปัจจุบันด้วยทางลัด ในบางจุด หน่วยความจำของ request ณ ตอนนั้นมองเห็นได้แล้วแต่ยังสร้างไม่เสร็จ และการออกฉุกเฉินทิ้งมันไว้ในสถานะที่ไม่ถูกต้อง จากนั้นขั้นตอนปิด request ก็เข้าไปอ่านสถานะนั้น ทีม PHP เขียนไว้ในรายละเอียดว่ายืนยันแล้วว่ารันโค้ดได้เต็มรูปแบบผ่าน HTTP กับ mod_php ที่ตั้งค่าเริ่มต้น
ตัวที่สอง “RCE via stale global state” เป็นเรื่องสถานะที่ worker เก็บไว้ระหว่าง request: ข้อผิดพลาด เซสชัน อ็อบเจกต์ ส่วนขยาย การทำงานบางส่วนเกิดขึ้นก่อนการเริ่มต้นตามปกติของ request ถัดไป และทันได้เข้าถึงข้อมูลที่เหลือจาก request ก่อนหน้าซึ่งถูกคืนหน่วยความจำไปแล้ว นี่คือการเข้าถึงหน่วยความจำที่ถูกปล่อยไปแล้ว และถ้าจัดเรียง heap ได้ลงตัว ก็รันโค้ดได้เช่นกัน ตัวนี้ยืนยันแล้วทั้งบน mod_php และ FPM รายละเอียดสำคัญ: request ที่ทิ้งขยะไว้กับ request ที่ไปเจอขยะนั้นต้องเข้า worker ตัวเดียวกัน การมีหลาย worker และการรีสตาร์ทโปรเซสช่วยลดโอกาส แต่ไม่ได้ทำให้ปัญหาหายไป
ในทางปฏิบัติหมายความว่าอะไร ทั้งสองกรณีไม่ต้องมีบัญชีบนเว็บไซต์ และไม่ต้องให้ผู้เข้าชมทำอะไรเลย ทั้งสองกรณีไม่ขึ้นกับ CMS: ช่องโหว่ไม่ได้อยู่ใน WordPress หรือในปลั๊กอิน แต่อยู่ในตัว PHP เอง ข้ออ้างว่า “เว็บเราเป็นเว็บธรรมดา ไม่มีอะไรให้เจาะหรอก” จึงใช้ไม่ได้ในกรณีนี้
และอีกรายละเอียดจากคำอธิบายช่องโหว่ตัวที่สอง ซึ่งตอบข้อโต้แย้งว่า “เว็บเราไม่มีเซสชันด้วยซ้ำ” หนึ่งในข้อต่อของห่วงโซ่ที่ยืนยันแล้วคือการติดตามความคืบหน้าการอัปโหลด session.upload_progress มันเปิดอยู่โดยค่าเริ่มต้น และทำงานได้โดยไม่ต้องมี session_start() ในโค้ดของเว็บไซต์เลย หมายความว่าเว็บไซต์ไม่จำเป็นต้องใช้เซสชันเองก็มีข้อต่อนี้อยู่ได้
เรื่องข่าวลือ และทำไมถึงไม่ควรเชื่อมันอยู่ดี
ควรบันทึกไว้แยกว่าเรื่องนี้หน้าตาเป็นยังไงก่อนรีลีส เพราะครั้งหน้าก็จะหน้าตาแบบเดียวกัน
ก่อนแพตช์ออก ทีม PHP ยืนยันแค่สี่อย่าง: รีลีสนอกรอบ วันและเวลา ช่องโหว่สองตัว และคะแนน 9.2 ตาม CVSS v4.0 ในประกาศไม่มีหมายเลข CVE คำอธิบาย และรายการเวอร์ชัน ส่วนที่เหลือทั้งหมด ไม่ว่าจะเป็นหมายเลข คำว่า RCE “โดนทุกเวอร์ชันตั้งแต่ PHP 5” หรือคะแนน “10.0 ตาม CVSS” ล้วนวนอยู่ในโซเชียลจากการเล่าต่อแบบไม่ระบุตัวตนที่อ้างถึง “แหล่งข่าวที่เชื่อถือได้”
สุดท้ายหมายเลขกับ RCE ได้รับการยืนยัน แต่ไม่ได้แปลว่าควรเชื่อตั้งแต่แรก: ตอนนั้นไม่มีอะไรให้ตรวจสอบ และข่าวลือที่ไม่เป็นจริงก็หน้าตาแบบนี้เป๊ะ อีกอย่าง ไม่ใช่ทุกข้อที่ได้รับการยืนยัน: ขอบเขต “ตั้งแต่ PHP 5” ไม่ได้รับการยืนยันอย่างเป็นทางการ ในรายละเอียดระบุแค่สายที่ยังซัพพอร์ต ส่วน backport ทีมทำให้แค่ 7.4, 8.0 และ 8.1 ไม่เก่ากว่านั้น และคะแนนยังเป็น 9.2 ไม่ใช่ 10.0
ตามมาด้วย “exploit ที่ใช้งานได้จริง” ที่แชร์กันในเน็ต เป็นไฟล์บีบอัดพร้อมสคริปต์ทดสอบ Ondřej Surý ผู้ดูแลรีโพแพ็กเกจ PHP ที่นิยมที่สุดสำหรับ Debian และ Ubuntu เตือนว่าไม่มีใครตรวจไฟล์นั้น มันดูเหมือน AI ประกอบขึ้นมา ถ้าใส่พารามิเตอร์ผิดมันก็แค่ทำให้โปรเซสล่ม และควรถือว่าเป็นของที่ไม่น่าไว้ใจ ข้อสรุปสำหรับเจ้าของเว็บไซต์ง่าย ๆ: ห้ามรัน “สคริปต์ตรวจช่องโหว่” จากโซเชียลบนเซิร์ฟเวอร์ของตัวเองเด็ดขาด นี่คือวิธีคลาสสิกที่จะพาของที่แย่กว่าตัวช่องโหว่เข้ามาเอง
ทำไมการผัดอัปเดตถึงอันตรายกว่าเมื่อก่อน
ผมไม่มีตัวเลขที่วัดตรง ๆ ว่า “จากแพตช์ PHP ถึงการโจมตีครั้งแรกใช้เวลาเท่าไหร่” และบอกไว้ตั้งแต่ตรงนี้จะตรงไปตรงมากว่า แต่เศรษฐศาสตร์ของการโจมตีโดยรวมเปลี่ยนไปในปีที่ผ่านมา และอันนี้มีการวัดแล้ว
ในบทความเรื่องงานวิจัยของ NorForge เกี่ยวกับแผงควบคุม Cairn ที่เปิดโล่ง ผมยกตัวเลขจากแคมเปญที่เล็งร้านค้าออนไลน์: ต้นทุนเฉลี่ยในการจัดการเป้าหมายหนึ่งรายราว 25 ดอลลาร์ การเลือกเหยื่อเป็นอัตโนมัติตามทราฟฟิก สัดส่วนความพยายามที่สำเร็จราวสิบเปอร์เซ็นต์ และจากเริ่มต้นจนได้ผลใช้เวลาไม่ถึงหนึ่งวัน กลไกคล้าย ๆ กันจากฝั่งบริการของมิจฉาชีพเห็นได้ในรายงานเรื่องฟิชชิงด้วย AI
ข้อสรุปสำหรับเจ้าของเว็บไซต์ง่าย ๆ เมื่อการลองหนึ่งครั้งมีต้นทุนแค่นี้ ก็ไล่ลองได้ทุกเว็บ รวมถึงเว็บเล็ก ๆ ที่ “ไม่มีอะไรให้เอา” ข้ออ้าง “ใครจะมาสนใจเรา” อยู่ได้เพราะเวลาของฝั่งผู้โจมตีเคยมีราคาแพง และช่วงเวลาที่เว็บไซต์ยังไม่ได้แก้ไขนั่นแหละคือสิ่งที่ผู้โจมตีหวังไว้
วิธีอัปเดต PHP: แผนทีละขั้นสำหรับเจ้าของเว็บไซต์
เริ่มจากการตรวจสอบก่อน ทุกคนต้องทำ และใช้เวลาแค่ไม่กี่นาที จากนั้นค่อยไปที่กรณีของคุณ
ขั้นตอนที่ 1: วิธีเช็กเวอร์ชัน PHP ของเว็บไซต์
วิธีที่ง่ายที่สุดในการเช็กเวอร์ชัน PHP คือดูในแผงควบคุมโฮสติ้ง ใน cPanel คือ MultiPHP Manager ใน FASTPANEL คือการ์ดของเว็บไซต์และการตั้งค่า PHP ในนั้น ใน ispmanager คือการตั้งค่าโดเมน ให้จำไว้สองเรื่อง: การตั้งค่าหนึ่งอาจมีผลกับหลายเว็บไซต์พร้อมกัน และเวอร์ชันสำหรับคอนโซลเลือกแยกจากเวอร์ชันที่เว็บไซต์ใช้งานจริง
ถ้าเว็บไซต์ใช้ WordPress เวอร์ชันจะเห็นในหน้าแอดมิน: Tools > Site Health > แท็บ Info > ส่วน Server ชื่อหัวข้ออาจต่างกันเล็กน้อยในแต่ละเวอร์ชัน ตรงนั้น WordPress ยังเตือนเรื่องสายที่ล้าสมัยด้วย แต่การไม่มีคำเตือนไม่ได้แปลว่าติดตั้งแพตช์ตัวนั้นแล้ว
บนเซิร์ฟเวอร์ของตัวเองมีกับดักที่คนสะดุดกันเป็นประจำ คำสั่ง php -v แสดงเวอร์ชันของ PHP ฝั่งคอนโซล แต่เว็บไซต์ทำงานผ่านโปรเซสแยกคือ PHP-FPM ซึ่งอาจเป็นคนละเวอร์ชัน ยิ่งกว่านั้น บนเครื่องอาจมี FPM หลายเวอร์ชัน และแต่ละเว็บไซต์อาจใช้ตัวต่างกัน ว่าเว็บไซต์ไหนใช้ตัวไหนจริง ๆ ดูได้จากการตั้งค่าเว็บเซิร์ฟเวอร์ ตาม socket หรือพอร์ตใน fastcgi_pass:
grep -rn fastcgi_pass /etc/nginx/
systemctl list-units 'php*-fpm.service'
คำสั่งแรกจะบอกว่าเว็บไซต์เรียก socket ตัวไหน คำสั่งที่สองบอกว่าบนเครื่องมีบริการ FPM อะไรรันอยู่บ้าง พอจับคู่กันแล้ว คุณจะได้คำตอบของคำถาม “เราใช้ PHP เวอร์ชันอะไร” ในแบบที่มีความหมายจริง ๆ
ขั้นตอนที่ 2: ใครเป็นคนอัปเดต PHP บนโฮสติ้ง บนเซิร์ฟเวอร์ของตัวเอง และใน Docker
โฮสติ้งทั่วไปหรือโฮสติ้งแบบ managed ผู้ให้บริการโฮสติ้งจะเป็นคนอัปเดต PHP คุณทำเองไม่ได้ หน้าที่ของคุณคืออย่านั่งรอเงียบ ๆ ดูหน้าข่าวหรือหน้าสถานะของผู้ให้บริการ หรือดีกว่านั้น เขียนถามซัพพอร์ตตรง ๆ ว่าโดเมนของเราใช้ PHP เวอร์ชันไหน ติดตั้งอัปเดตความปลอดภัยของวันที่ 21 ตุลาคม (8.2.35, 8.3.36, 8.4.27 หรือ 8.5.12) แล้วหรือยัง ถ้ายัง จะติดตั้งเมื่อไหร่ และเราต้องทำอะไรไหม ระยะเวลาของผู้ให้บริการแต่ละเจ้าต่างกันมาก ตั้งแต่ไม่กี่ชั่วโมงไปจนถึงรอบบำรุงรักษาตามแผนครั้งถัดไป และคำตอบของคำถามนี้คือวิธีเดียวที่จะรู้ว่าของคุณเป็นแบบไหน
คำถามที่เจ้าของ WordPress ถามบ่อยคือจะอัปเดต PHP จากหน้าแอดมินได้ยังไง คำตอบคือไม่ได้: WordPress แค่แสดงเวอร์ชันใน Site Health ส่วนการเปลี่ยนเวอร์ชันทำในแผงควบคุมโฮสติ้ง ภายในสายเดียวกัน เช่นจาก 8.3.35 เป็น 8.3.36 ผู้ให้บริการโฮสติ้งเป็นคนทำ และปกติอัปเดตแบบนี้จะผ่านไปโดยไม่มีใครสังเกต แต่ “ปกติ” ไม่ใช่การรับประกัน: หลังอัปเดตควรเปิดเว็บไซต์ แล้วเช็กฟอร์มและการชำระเงินเหมือนในขั้นตอนที่ห้า ส่วนถ้าต้องย้ายไปสายอื่น เช่นจาก 7.4 หรือ 8.1 ไปสายที่ยังซัพพอร์ต ให้อัปเดต WordPress ธีม และปลั๊กอินก่อน เพราะปลั๊กอินเก่านี่แหละที่พังบ่อยที่สุดบน PHP เวอร์ชันใหม่
VPS หรือเซิร์ฟเวอร์เฉพาะของตัวเอง สิ่งแรกคือหาว่าแพ็กเกจ PHP ของคุณมาจากไหน เพราะมันกำหนดว่าการแก้ไขจะมาถึงเมื่อไหร่
ถ้ามาจากรีโพภายนอกของ Ondřej Surý คือ packages.sury.org สำหรับ Debian หรือ ppa:ondrej/php สำหรับ Ubuntu (บนเซิร์ฟเวอร์ที่มี PHP หลายเวอร์ชัน แทบจะเป็นรีโพนี้เสมอ) การแก้ไขจะถูก build ในวันที่รีลีส ผู้ดูแลรีโพสัญญาว่าจะออกให้ทุกสายที่มีในรีโพ และทุกรีลีสของ Debian และ Ubuntu ที่ยังซัพพอร์ต: เริ่มจากเซิร์ฟเวอร์ทั่วไปบน amd64 ก่อน ส่วน build สำหรับ ARM ตามมาทีหลัง เขาไม่ได้บอกกรอบเวลา และวันนั้นเซิร์ฟเวอร์ build ก็รับโหลดหนักมาก
ถ้า PHP มาจากรีโพมาตรฐานของดิสโทร การแก้ไขจะมาทางอัปเดตความปลอดภัยตามปกติ แต่ไม่ใช่ทันที ตอนเย็นวันที่ 21 ตุลาคม แพ็กเกจที่ได้รับผลกระทบทั้งหมดของ Ubuntu ยังอยู่ในสถานะ “Needs evaluation” และในตัวติดตามของ Debian ก็ยังไม่มีรายการของ CVE เหล่านี้ ในกรณีนี้มาตรการชั่วคราวในหัวข้อด้านล่างไม่ใช่ทางเลือก แต่เป็นสิ่งที่ต้องทำระหว่างรอแพ็กเกจออก
ดูได้ว่าติดตั้งอะไรไว้และมีอะไรให้อัปเดตด้วยคำสั่งเหล่านี้:
sudo apt update
apt list --upgradable
apt-cache policy php8.3-fpm # ใส่เวอร์ชันของคุณ
apt-mark showhold
ในผลลัพธ์ของ apt-cache policy บรรทัดแรกคือเวอร์ชันที่ติดตั้งอยู่ บรรทัดที่สองคือเวอร์ชันที่จะอัปเดตไป และด้านล่างบอกว่าจะมาจากรีโพไหน apt-mark showhold ใช้เช็กกับดัก: แพ็กเกจที่ใครสักคนเคย “แช่แข็ง” ไว้จะไม่อัปเดตทั้งแบบอัปเดตทั้งหมดทีเดียวและแบบเจาะจง และจะดูเหมือนไม่มีอะไรให้อัปเดต ถ้า PHP อยู่ในรายการนี้ ให้ปลดการแช่แข็งด้วย sudo apt-mark unhold ตามด้วยชื่อแพ็กเกจ
ทำแบ็กอัปก่อนรันคำสั่งถัดไป ไม่ใช่หลังจากนั้น: เรื่องแบ็กอัปมีรายละเอียดในขั้นตอนที่สี่ แต่ต้องทำตรงนี้ ถ้าอัปเดตแล้วไปขัดกับส่วนขยาย ก็จะย้อนกลับไม่ได้แล้ว
ถ้าในรายการมีแต่แพ็กเกจ PHP จะอัปเดตทั้งหมดทีเดียวก็ได้ (sudo apt upgrade) แต่ถ้ามีครึ่งระบบที่วันนี้คุณไม่ได้ตั้งใจจะแตะอยู่ในนั้นด้วย ให้อัปเดตแบบเจาะจง โดยระบุแพ็กเกจ PHP ของคุณพร้อมส่วนขยาย:
sudo apt install --only-upgrade php8.3-fpm php8.3-cli php8.3-common
จากนั้นต้องรีสตาร์ทบริการ FPM ที่ให้บริการเว็บไซต์ ถ้ามีหลายเวอร์ชันก็ต้องรีสตาร์ททุกตัว:
sudo systemctl restart php8.3-fpm
ตอนนี้ไม่ต้องแตะ Nginx: มันเรียก FPM ผ่าน socket และการอัปเดต PHP ไม่ได้เปลี่ยนการตั้งค่าของมัน ต้องโหลดการตั้งค่าใหม่ก็ต่อเมื่อคุณแก้มัน และถ้าแก้ ให้รัน sudo nginx -t ก่อน แต่ถ้า PHP ของคุณต่อกับ Apache เป็นโมดูล (mod_php) ไม่ใช่ผ่าน FPM ต้องรีสตาร์ทตัว Apache เอง เพราะโมดูลอยู่ในโปรเซสของมัน
อีกเรื่องคือโปรเซส PHP ที่รันยาวนอก FPM: Laravel Octane, Swoole, RoadRunner, ตัวประมวลผลคิว และ daemon ที่รันใต้ supervisor พวกนี้ถือ PHP เวอร์ชันที่ใช้ตอนเริ่มทำงานไว้ในหน่วยความจำ และแพ็กเกจที่อัปเดตบนดิสก์ไม่มีผลกับมัน ให้รีสตาร์ทพวกนี้ด้วย โดยเริ่มจากตัวที่รับ HTTP request เอง
สำหรับ RHEL, AlmaLinux และ Rocky ขั้นตอนเหมือนกันผ่าน dnf ปกติจะใช้คู่กับรีโพ Remi
PHP ใน Docker อัปเดตของอิมเมจฐานไม่ได้มาถึงเอง: คอนเทนเนอร์จะทำงานต่อบนเวอร์ชันที่ build ไว้ แบ็กอัปใช้กฎเดียวกับข้างบน: ก่อน ไม่ใช่หลัง
ก่อนอื่นเปิดดู Dockerfile และ compose.yaml ว่าเขียนเวอร์ชันไว้ยังไง ถ้าเป็น FROM php:8.3-fpm อิมเมจใหม่จะถูกดึงมา แต่ถ้าเวอร์ชันถูกตอกตายไว้ เช่น php:8.3.35-fpm หรืออ้างด้วย digest ไม่มีคำสั่งไหนขยับคุณออกจากเวอร์ชันนั้นได้: tag ชี้ไปที่ build เฉพาะตัว และ --pull ก็จะเช็กอัปเดตให้ build ตัวนั้นตามจริง บรรทัดแบบนี้ต้องแก้ด้วยมือให้เป็นเวอร์ชันที่แก้แล้ว แล้วค่อยอัปเดต
ต่อจากนี้ขึ้นอยู่กับว่าบริการถูก build ยังไง ถ้าคุณมี Dockerfile ของตัวเองที่มี FROM php:...:
docker compose build --pull php
docker compose up -d php
ถ้าใน compose.yaml ระบุอิมเมจสำเร็จรูปผ่าน image::
docker compose pull php
docker compose up -d php
ตรงนี้ php คือชื่อบริการในไฟล์ Compose ของคุณ ให้ใส่ชื่อของคุณเอง แฟล็ก --pull สำคัญ: ถ้าไม่ใส่ การ build อาจใช้อิมเมจฐานเก่าจากแคชในเครื่อง และ --no-cache ไม่ได้แก้ปัญหานี้ มัน build เลเยอร์ใหม่ แต่ไม่อัปเดตอิมเมจฐาน ส่วนถ้าอิมเมจ build ใน CI อย่าลืมว่าการ build เป็นแค่ครึ่งเดียวของงาน: อิมเมจใหม่ยังต้องเผยแพร่และ deploy ส่วนคอนเทนเนอร์ที่รันอยู่จะไม่อัปเดตตัวเอง
และกับดักอีกข้อที่ทำให้เว็บไซต์ล่มได้หลังอัปเดตสำเร็จแล้ว ถ้า nginx ของคุณอยู่ในคอนเทนเนอร์ข้าง ๆ และเรียก PHP ด้วยชื่อบริการ (fastcgi_pass php:9000) คอนเทนเนอร์ที่สร้างใหม่จะได้แอดเดรสใหม่ แต่ nginx ยังไปเคาะที่แอดเดรสเก่า และแทนที่จะเห็นเว็บไซต์ คุณจะเห็น 502 แก้ได้ด้วยการรีสตาร์ทฟรอนต์เอนด์ตามไป:
docker compose restart nginx
เช็กผลลัพธ์ในคอนเทนเนอร์:
docker compose exec php php -v
ขั้นตอนที่ 3: PHP เวอร์ชันไหนยังใช้ได้ และเวอร์ชันของคุณหมดซัพพอร์ตแล้วหรือยัง
นี่เป็นข้อแยกต่างหาก และในความเห็นของผม เป็นข้อที่สำคัญที่สุดสำหรับเว็บไซต์เล็ก ๆ
PHP เวอร์ชันที่ยังใช้ได้คือเวอร์ชันที่โปรเจกต์ซัพพอร์ตเอง: ตอนนี้คือ 8.2, 8.3, 8.4 และ 8.5 เวอร์ชันเสถียรล่าสุดคือ 8.5 ส่วน 8.6 ยังอยู่ในขั้น release candidate และยังไม่เหมาะกับเว็บไซต์ที่ใช้งานจริง สาย 8.1 และเก่ากว่าถูกประกาศ end of life แล้ว: ทีม PHP ไม่ออกรีลีสปกติให้อีก แต่ครั้งนี้ทีมทำข้อยกเว้น ซึ่งจะเล่าด้านล่าง
ข้อแม้สำคัญ ที่ถ้าไม่พูดไว้ ข้อนี้จะฟังดูน่ากลัวกว่าความเป็นจริง ดิสโทรและผู้ให้บริการเชิงพาณิชย์มักดูแลสายเก่าเอง: Debian, Ubuntu และ RHEL นำการแก้ไขความปลอดภัยมาใส่ในแพ็กเกจของตัวเองตลอดอายุของรีลีส OS ดังนั้นเว็บไซต์บน PHP 7.4 จากรีโพของระบบอาจได้การแก้ไขจากผู้ให้บริการ OS หรืออาจไม่ได้ก็ได้ วิธีเดียวที่จะรู้คือถามคนที่ส่งแพ็กเกจเหล่านี้ให้คุณ: ผู้ให้บริการโฮสติ้ง ผู้ให้บริการ OS หรือผู้รับเหมา
ทีม PHP เองก็ทำข้อยกเว้นด้วย เพราะช่องโหว่ร้ายแรง ทีมจึงปล่อย backport การแก้ไขแบบขั้นต่ำในรูปซอร์สโค้ดให้สามสายที่หยุดซัพพอร์ตแล้ว: PHP 7.4, PHP 8.0 และ PHP 8.1 ในรีโพ php-src ต้องเข้าใจว่ามันคืออะไร มันไม่ใช่รีลีส: จะไม่มีหมายเลขเวอร์ชันใหม่และไม่มีแพ็กเกจสำเร็จรูป ต้อง build แพตช์เอง มันทำมาสำหรับคนที่ build PHP จากซอร์สโค้ด หรือคนที่ไม่มีผู้ให้บริการที่ดูแลสายเก่าให้ สำหรับเจ้าของเว็บไซต์บนโฮสติ้งทั่วไป มันไม่ช่วยอะไร: ตรงนั้นยังต้องถามผู้ให้บริการโฮสติ้งเหมือนเดิม และมันไม่ครอบคลุม PHP 7.3 และเก่ากว่า
ตัวอย่างที่ดีคือรีโพ sury ตัวเดิม ที่นั่นสัญญาว่าจะแก้ CVE สองตัวนี้ให้ทุกสายที่มีในรีโพ ไม่ใช่แค่สายที่ทีม PHP ซัพพอร์ต จากที่ผู้ใช้รายงาน ในวันที่รีลีสมีอัปเดตมาทั้งสำหรับ 7.4, 8.0 และ 8.1 และแม้แต่ 5.6 นี่ไม่ใช่เหตุผลที่จะอยู่บน 7.4 ต่อ: ช่องโหว่ถัดไปในสายเก่าอาจไม่มีใครปิดให้แล้ว แต่ ณ ตอนนี้ มันคือคำตอบของคำถาม “ต้องทำอะไรตอนนี้เลย”
ปฏิทินซัพพอร์ตก็ควรดูด้วย สาย 8.2 จะหมดการแก้ไขความปลอดภัยในวันที่ 31 ธันวาคม 2569 อีกสองเดือนมันจะไปอยู่ตรงที่ 8.1 อยู่ตอนนี้ สาย 8.3 ซัพพอร์ตถึงปลายปี 2570, 8.4 ถึงปลายปี 2571 และ 8.5 ถึงปลายปี 2572 ถ้าคุณใช้ 8.2 ควรเตรียมย้ายตั้งแต่ตอนนี้: เลือกสายที่เข้ากับ CMS ปลั๊กอิน และส่วนขยายของคุณ แล้วทดสอบการย้ายให้เสร็จก่อนสิ้นปี ไม่ใช่ในสัปดาห์สุดท้ายของเดือนธันวาคม
สำหรับเว็บไซต์ที่อยู่บนสาย EOL ลำดับขั้นตอนคือ ทำแบ็กอัปและเตรียมวิธีย้อนกลับที่ชัดเจนก่อน จากนั้นทดสอบบนสำเนาของเว็บไซต์ แล้วค่อยสลับเว็บไซต์จริง การสลับเวอร์ชันในแผงควบคุมเฉย ๆ ไม่เท่ากับการติดตั้งแพตช์: ตรวจให้แน่ใจว่าคุณย้ายไปยัง build ล่าสุดของสายที่ยังซัพพอร์ต ไม่ใช่ build ของปีที่แล้ว
ขั้นตอนที่ 4: แบ็กอัปก่อนอัปเดต และเช็กว่ากู้คืนได้จริง
เรื่องพื้น ๆ ที่ช่วยชีวิตได้ อัปเดตภายในสายเดียวกันมักผ่านไปเงียบกว่าการย้ายไปสายใหม่ แต่ก็ไม่ใช่การรับประกัน: บนเว็บไซต์อาจมีส่วนขยายที่คอมไพล์มาสำหรับ build ของ PHP ตัวใดตัวหนึ่งโดยเฉพาะ
สำเนาต้องมีทั้งไฟล์และฐานข้อมูล ต้องไม่อยู่บนเซิร์ฟเวอร์เดียวกัน และต้องกู้คืนได้ สำเนาที่ไม่เคยลองกู้คืนไม่ใช่แบ็กอัป แต่เป็นความหวัง สำหรับร้านค้าออนไลน์ให้เพิ่มอีกคำถามหนึ่ง: จะทำยังไงกับออร์เดอร์ที่เข้ามาระหว่างตอนทำสำเนากับตอนย้อนกลับ เพราะการกู้ฐานข้อมูลของเมื่อวานจะลบมันทิ้ง
และอีกเรื่องที่คนมักนึกได้ช้า: แบ็กอัปต้องป้องกันไม่ให้ถูกลบด้วยบัญชีเดียวกับที่เว็บไซต์ใช้ทำงาน ในบทวิเคราะห์แคมเปญที่เล็งร้านค้าออนไลน์ มีเหยื่อรายหนึ่งที่ระบบอัตโนมัติลบตารางแบ็กอัปไปด้วย
ขั้นตอนที่ 5: วิธีเช็กว่าอัปเดตติดตั้งแล้ว
หลังอัปเดต ดูเวอร์ชันของโปรเซสที่ให้บริการเว็บไซต์ และหมายเลขเต็มของแพ็กเกจที่ติดตั้ง ข้อควรรู้: ถ้าการแก้ไขมาจากผู้ให้บริการ OS หมายเลขเวอร์ชัน PHP อาจไม่เปลี่ยนเลย สิ่งที่เปลี่ยนคือหมายเลขแพ็กเกจ ในกรณีนี้ หลักฐานยืนยันคือประกาศความปลอดภัยของผู้ให้บริการ ไม่ใช่ผลลัพธ์ของ php -v
จากนั้นเปิดเว็บไซต์ และไม่ใช่แค่หน้าแรก หน้าแอดมิน ฟอร์มชำระเงิน การอัปโหลดไฟล์ การส่งอีเมล: นี่คือจุดที่ส่วนขยายที่ไม่เข้ากันจะโผล่ออกมา และเปิดดู error log ของเว็บเซิร์ฟเวอร์ด้วย มันจะแสดงสิ่งที่มองไม่เห็นบนหน้าเว็บ
ถ้ายังอัปเดต PHP ตอนนี้ไม่ได้ ต้องทำยังไง
เรื่องแบบนี้เกิดขึ้นได้: ผู้รับเหมาติดต่อไม่ได้ เว็บไซต์เก่ามาก ช่วงบำรุงรักษาทำได้แค่วันหยุดสุดสัปดาห์ ถ้าอย่างนั้นก็ใช้มาตรการที่ลดความเสี่ยง แต่ไม่ได้แทนแพตช์ ทีม PHP เขียนไว้ตรง ๆ ในรายละเอียดทั้งสองฉบับและเน้นเป็นคำเตือน: ทางแก้ที่สมบูรณ์มีทางเดียวคืออัปเดต อย่างอื่นแค่ตัดข้อต่อบางข้อ แต่มาตรการเหล่านี้มีรายละเอียดชัดเจน และบางข้อก็ไม่ตรงกับที่คาด นี่คือสิ่งที่ทีม PHP แนะนำเองสำหรับคนที่ยังอัปเดตไม่ได้
สำหรับช่องโหว่ตัวแรกเรื่องหน่วยความจำหมด การตั้งค่าหลักคือกำหนด memory_limit=-1 ในระดับผู้ดูแลระบบ แล้วรีสตาร์ท worker ทั้งหมด ใช่ ยกเลิกลิมิต ไม่ใช่ทำให้เข้มขึ้น: เพราะลิมิตต่อ request ที่คาดเดาได้นี่แหละที่ทำให้ฝั่งผู้โจมตีควบคุมสถานการณ์โจมตีที่ยืนยันแล้วได้ ก่อนทำ ควรประเมินตามจริงว่าเซิร์ฟเวอร์รับ request ที่ไม่มีเพดานหน่วยความจำไหวไหม และเช็กว่าไม่มี pool ไดเรกทอรี หรือการตั้งค่าของแอปไหนที่ใส่ลิมิตจำกัดกลับเข้ามา ในคำแนะนำเดียวกันยังมีคำเตือนแยกว่า อย่าจำกัดหน่วยความจำด้วย RLIMIT_AS และ RLIMIT_DATA เพราะมันทำให้การจองหน่วยความจำล้มเหลวแบบเดียวกับที่ไปกระตุ้นเส้นทางที่มีช่องโหว่ ลิมิตหน่วยความจำของคอนเทนเนอร์หรือ cgroup อาจใช้แทนได้ แต่มีข้อแม้ และก็ไม่สมบูรณ์เช่นกัน
สิ่งที่ช่วยลดความเสี่ยงแม้หลังติดตั้งแพตช์ก็มีประโยชน์: ตั้ง post_max_size ให้ไม่ใหญ่เกินไป เช็กขนาดจริงของ body ของ request ที่ฟรอนต์เอนด์ ลดความซ้อนและจำนวนตัวแปรอินพุต และถ้าแอปไม่รับฟอร์มแบบ multipart เลย ให้ตัด request แบบนี้ทิ้งก่อนที่จะถึง PHP ใน nginx ใช้แค่ไม่กี่บรรทัดในบล็อก server:
if ($http_content_type ~* "multipart") {
return 403;
}
ราคาของมาตรการนี้คือการอัปโหลดไฟล์ทุกแบบและฟอร์มที่ส่งข้อมูลแบบ multipart จะใช้ไม่ได้ ก่อนเปิดใช้ ให้ไล่เช็กฟอร์มทั้งหมดของเว็บไซต์
สำหรับช่องโหว่ตัวที่สองเรื่องสถานะระหว่าง request ไม่มีการตั้งค่าแบบครอบคลุมเลย และทีมก็บอกไว้ตรง ๆ มาตรการเฉพาะจุดคือ บน PHP 8.5 ให้ปิด fatal_error_backtraces ทุกคนให้ปิด session.upload_progress.enabled ถ้าไม่ได้ใช้ (ทีม PHP แนะนำให้ปิดไว้แม้หลังอัปเดต: ฟีเจอร์นี้มีคนใช้น้อยและยังไม่ผ่านการใช้งานมากพอ) แล้วรีสตาร์ท worker ถ้าประสิทธิภาพพอรับไหว pm.max_requests=1 ช่วยได้: worker มีอายุแค่หนึ่ง request และไม่ทันพาอะไรติดไปยัง request ถัดไป และควรจัดการ fatal error กับ warning บนเส้นทางที่ผู้เข้าชมเข้าถึงได้ เพราะสิ่งเหล่านี้คือตัวที่ทิ้งขยะไว้
คำว่า “ในระดับผู้ดูแลระบบ” ในคำแนะนำเหล่านี้เป็นคำสำคัญ การตั้งค่าต้องทำในจุดที่แอปเขียนทับไม่ได้: ใน php.ini หรือใน pool ของ FPM เช่นแบบนี้:
; /etc/php/8.3/fpm/pool.d/www.conf — ใส่เวอร์ชันและ pool ของคุณ
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[memory_limit] = -1
; เฉพาะ PHP 8.5
php_admin_flag[fatal_error_backtraces] = off
หลังแก้ ให้รีสตาร์ท FPM แต่ .htaccess ที่ใช้ php_value และ php_flag รวมถึงพารามิเตอร์ PHP_VALUE ที่ nginx ส่งไปใน FastCGI ใช้กับเรื่องนี้ไม่ได้ ทีม PHP แนะนำตรง ๆ ให้ใช้การตั้งค่าที่มีผลตอนเริ่มทำงาน: ค่าที่มากับแต่ละ request จะถูกประมวลผลก่อนการเริ่มต้นของ request นั้น และ directive ที่ล้าสมัยซึ่งออก warning ตรงนั้นคือหนึ่งในข้อต่อของห่วงโซ่ที่สอง ควรล้าง directive เก่าและที่ผิดออกจากตรงนั้นให้หมด
และสิ่งที่ไม่ควรทำแม้จะตื่นตระหนกแค่ไหน คือ “ปิด PHP” ในเว็บเซิร์ฟเวอร์ ถ้าเอา handler ออก nginx หรือ Apache จะเริ่มส่งไฟล์ .php ออกไปเป็นข้อความธรรมดา พร้อมรหัสผ่านฐานข้อมูลจากไฟล์ตั้งค่า ถ้าต้องปิดเว็บไซต์ระหว่างอัปเดต ให้เว็บเซิร์ฟเวอร์ตอบด้วยโค้ด 503
ที่เหลือคือสุขอนามัยพื้นฐาน ซึ่งสำคัญมาตั้งแต่ก่อนเรื่องนี้แล้ว
ลดสิ่งที่โผล่ออกสู่ภายนอก หน้าแอดมินที่เข้าได้จากทุกที่ phpMyAdmin บนแอดเดรสมาตรฐาน ซับโดเมนทดสอบที่ลืมไว้พร้อมสำเนาเว็บไซต์ ทั้งหมดนี้คือพื้นผิวการโจมตีที่ไม่จำเป็น เรื่องพอร์ต ไฟร์วอลล์ และสิ่งที่ควรปิดเป็นค่าเริ่มต้น ผมเขียนไว้ละเอียดในบทความเรื่องการเริ่มรันบริการใน Docker อย่างปลอดภัย ตอนจำกัดการเข้าถึง อย่าเผลอตัดตัวเองและอินทิเกรชันที่จำเป็นออกไปด้วย: คิดถึงเน็ตมือถือที่แอดเดรสเปลี่ยนไปเรื่อย ๆ และการแจ้งเตือนการชำระเงินที่มาจากภายนอก
ดู WAF ถ้ามีในแพ็กเกจโฮสติ้งหรือถ้าคุณใช้ Cloudflare อยู่: รายละเอียดเผยแพร่แล้ว และผู้ให้บริการด้านการป้องกันมักตอบสนองต่อเรื่องแบบนี้ ควรเช็กว่ามีกฎใหม่ขึ้นมาหรือยังและเปิดใช้อยู่หรือเปล่า แต่ต้องเข้าใจว่าช่องโหว่ทั้งสองตัวทำงานกับ request ที่ดูธรรมดา จึงไม่ควรคาดหวังจากตัวกรองมากนัก และ WAF จะได้ผลก็ต่อเมื่อเข้าถึงเซิร์ฟเวอร์โดยเลี่ยง WAF ไม่ได้: ถ้า IP ของเว็บไซต์เป็นที่รู้กันและเปิดให้ทุกคนเข้าถึง ตัวกรองบน Cloudflare ก็จะถูกเลี่ยงไปง่าย ๆ
ตั้งระบบเฝ้าดูไฟล์ ไฟล์ในโฟลเดอร์รากของเว็บไซต์ที่เปลี่ยนไปโดยไม่คาดคิด หรือสคริปต์ใหม่จากโดเมนที่ไม่รู้จัก คือสิ่งที่ควรถูกจับได้อัตโนมัติ ไม่ใช่ตอนที่คุณเข้าหน้าแอดมินครั้งถัดไป
และกำหนดเส้นตายให้ตัวเอง “เดี๋ยวว่างค่อยอัปเดต” คือทางเลือกที่แย่ที่สุด: มันเปลี่ยนมาตรการชั่วคราวให้กลายเป็นของถาวร
วิธีเช็กว่าโดนแฮกหรือเปล่า
รายละเอียดของช่องโหว่หลุดออกมาหนึ่งวันก่อนแพตช์ และทีม PHP เองก็ยืนยันว่ารันโค้ดผ่าน HTTP ได้ ในขณะเดียวกัน ณ เวลาที่เผยแพร่บทความ ยังไม่มีรายงานที่ยืนยันได้เรื่องการโจมตีเป็นวงกว้าง: มีคนอ้างเรื่องนี้ แต่ไม่มีใครแสดงข้อมูลสาธารณะประกอบ ยังเร็วไปที่จะตื่นตระหนก แต่เป็นเวลาที่เหมาะจะเข้าไปตรวจดู โดยเฉพาะถ้าก่อนอัปเดตเว็บไซต์เปิดให้ทุกคนเข้าถึงได้
สิ่งที่ต้องดูก่อน:
- โปรเซสของเว็บเซิร์ฟเวอร์: php-fpm หรือ Apache ที่เปิด shell ขึ้นมาเป็นสัญญาณไม่ดี
- ไฟล์
.phpใหม่และที่ถูกแก้ไขในโฟลเดอร์รากของเว็บไซต์ และโดยเฉพาะในโฟลเดอร์อัปโหลด - งานของตัวจัดตารางงาน:
crontab -lในนามผู้ใช้ของเว็บเซิร์ฟเวอร์ และไดเรกทอรี/etc/cron.*การฝังตัวไว้ตรงนั้นเป็นเทคนิคมาตรฐาน - access log: request
POSTที่มีmultipart/form-dataพุ่งขึ้นผิดปกติไปยังหน้าที่ไม่รับการอัปโหลดเลย
หาไฟล์ PHP ใหม่ในช่วงสามวันที่ผ่านมาได้ด้วยคำสั่งเดียว (ใส่พาธของคุณ):
find /var/www -name '*.php' -mtime -3 -ls
ถ้าเจออะไร การอัปเดตอย่างเดียวไม่พอ: แพตช์ไม่ได้ยึดสิทธิ์เข้าถึงที่ผู้โจมตีได้ไปแล้วคืนมา ต้องเปลี่ยนรหัสผ่านและคีย์ ตรวจไฟล์ และมีแนวโน้มว่าต้องกู้คืนจากสำเนาที่ทำไว้ก่อนเกิดปัญหา
เช็กลิสต์: อัปเดต PHP ในสิบขั้นตอน
สรุปสั้น ๆ เรียงตามลำดับ:
- หาว่าเว็บไซต์ใช้ PHP ตัวไหน: แผงควบคุมโฮสติ้ง Site Health ใน WordPress หรือ
fastcgi_passประกอบกับรายการบริการ FPM - ถ้าเว็บไซต์ส่งแค่ไฟล์สำเร็จรูปและไม่มีโค้ดฝั่งเซิร์ฟเวอร์ ไม่ต้องอ่านต่อ
- เช็กสาย: ต้องเป็น 8.2.35, 8.3.36, 8.4.27 หรือ 8.5.12 อะไรที่ต่ำกว่านั้นมีช่องโหว่ บน 7.4–8.1 ใช้แพ็กเกจจากผู้ให้บริการแพ็กเกจ หรือ backport ของทีม PHP สำหรับ build จากซอร์สโค้ด ส่วนที่เก่ากว่านั้นอีก มีแค่ผู้ให้บริการแพ็กเกจหรือการย้ายสาย
- ระบุว่าใครเป็นคนอัปเดต: ผู้ให้บริการโฮสติ้งหรือคุณ
- บนโฮสติ้ง: ถามซัพพอร์ตเรื่องโดเมน แพตช์ และสิ่งที่คุณต้องทำ
- บนเซิร์ฟเวอร์ของตัวเอง: หาว่าแพ็กเกจมาจากไหน (sury หรือดิสโทร) เช็ก
apt-mark showholdทำแบ็กอัป อัปเดตแพ็กเกจ PHP รีสตาร์ทบริการ FPM ทั้งหมดและโปรเซส PHP ที่รันยาว - ใน Docker: เช็กว่าเวอร์ชันถูกตอกตายใน tag หรือเปล่า build ใหม่ด้วย
--pullหรือดึงอิมเมจสำเร็จรูป deploy ใหม่ รีสตาร์ทฟรอนต์เอนด์ และเช็กเวอร์ชันในคอนเทนเนอร์ - หลังอัปเดต: เวอร์ชัน หมายเลขแพ็กเกจ เว็บไซต์ทั้งเว็บ (หน้าแอดมิน การชำระเงิน อีเมล) และ error log
- ถ้าอัปเดตไม่ได้: ใช้มาตรการชั่วคราวจากรายละเอียดของ PHP ใน
php.iniหรือ pool ของ FPM ลดพื้นผิวที่เปิดสู่ภายนอก และกำหนดวัน อย่าปิด PHP ในเว็บเซิร์ฟเวอร์ ให้ตอบ 503 แทน - ถ้าเว็บไซต์เปิดอยู่ก่อนอัปเดต: เช็กโปรเซส ไฟล์ใหม่ งาน cron และ access log
อะไรจะยังอยู่หลังจากนี้
ช่องโหว่สองตัวนี้จะถูกปิด และอีกสัปดาห์คนก็ลืม แต่จะมีอย่างอื่นเหลืออยู่
ข้อแรก: การทำ inventory คำตอบที่แพงที่สุดของคำถาม “อัปเดตแล้วหรือยัง” คือ “แล้ว PHP ของเราอยู่ตรงไหนบ้างล่ะ” รายการว่าโค้ดฝั่งเซิร์ฟเวอร์ของคุณรันอยู่ที่ไหน พร้อมเวอร์ชันและคนรับผิดชอบ ทำครั้งเดียวแล้วคอยอัปเดต และในวันแบบนี้มันประหยัดเวลาได้หลายชั่วโมง
ข้อที่สอง: สายที่ตายแล้ว ทีม PHP เลิกซัพพอร์ต PHP 7.4 มานานแล้ว และถ้าไม่มีผู้ให้บริการดูแลแยกต่างหาก มันก็จะมีช่องโหว่ที่ไม่ได้แก้ค้างอยู่ แพตช์นอกรอบเป็นโอกาสดีที่จะดูตัวเลขเวอร์ชัน และใส่การย้ายไว้ในแผนก่อนที่มันจะกลายเป็นเรื่องด่วน
และข้อที่สาม ที่ไม่ค่อยชัดนัก การเตือนล่วงหน้าหนึ่งวันคือการยอมรับว่าความเร็วในการติดตั้งแพตช์สำคัญกว่าความเงียบรอบช่องโหว่แล้ว จากมุมของเจ้าของเว็บไซต์ คำตอบที่ถูกต้องไม่ใช่การตื่นตระหนกในวันรีลีส แต่คือลำดับขั้นตอนที่รู้ไว้ล่วงหน้า: ใครอัปเดต ในช่วงเวลาไหน ด้วยแบ็กอัปแบบไหน และเช็กผลลัพธ์ยังไง เขียนสิ่งนี้ไว้ครั้งเดียวคุ้มกว่าการด้นสดทุกครั้ง
บทความนี้เขียนจากประกาศของทีมความปลอดภัย PHP วันที่ 20 ตุลาคม 2569 รายละเอียดช่องโหว่ GHSA-w8hw-w9fx-j79w และ GHSA-6wmf-4rv5-p3rp ในรีโพ php-src บันทึกการเปลี่ยนแปลงของ PHP 8 คำตอบของผู้ดูแลรีโพ deb.sury.org และข้อมูลจาก Ubuntu Security




