เว็บไซต์ที่เขียนด้วย PHP เช่น WordPress, Joomla หรือระบบที่บริษัทพัฒนาเอง มีภัยหนึ่งที่เงียบแต่ร้ายแรงที่สุด คือ PHP web shell (บางคนเรียกสั้น ๆ ว่า "PHP shell") ไฟล์ขนาดไม่กี่บรรทัดที่ถ้าหลุดเข้าไปอยู่บนเซิร์ฟเวอร์ได้ ผู้โจมตีจะสั่งงานเครื่องนั้นผ่านเบราว์เซอร์ได้ทันที ทั้งอ่านฐานข้อมูล แก้หน้าเว็บ ส่งสแปม หรือใช้เป็นทางเข้าไปยังระบบอื่นในองค์กร บทความนี้อธิบายว่า web shell เข้ามาได้อย่างไร สังเกตอย่างไร และ Cloudflare ช่วยป้องกันได้ตรงไหน รวมถึงตรงไหนที่ Cloudflare ช่วยไม่ได้

PHP web shell คืออะไร

web shell คือสคริปต์ที่ผู้โจมตีวางไว้บนเว็บเซิร์ฟเวอร์ เมื่อเรียก URL ของไฟล์นั้นพร้อมคำสั่ง เซิร์ฟเวอร์จะรันคำสั่งนั้นด้วยสิทธิ์ของโปรแกรมเว็บเซิร์ฟเวอร์ เพราะ PHP มีฟังก์ชันอย่าง system, exec, shell_exec และ eval ไฟล์ web shell จึงสั้นมาก และมักซ่อนตัวด้วยการเข้ารหัส base64 ตั้งชื่อให้ดูเหมือนไฟล์ระบบ หรือแทรกอยู่ในไฟล์ PHP เดิมของเว็บ

สิ่งที่ทำให้อันตรายคือ web shell ใช้ HTTP ธรรมดา ผ่านพอร์ต 80/443 แบบเดียวกับผู้เข้าชมเว็บ ไฟร์วอลล์ทั่วไปจึงมองไม่เห็นความผิดปกติ และหลายครั้งเจ้าของเว็บรู้ตัวก็ต่อเมื่อ Google ขึ้นเตือนว่าเว็บไม่ปลอดภัย หรือผู้ให้บริการโฮสติ้งระงับบัญชีเพราะเครื่องส่งสแปม

web shell เข้ามาได้ทางไหน

  • ฟอร์มอัปโหลดที่ตรวจไฟล์ไม่ดี เช่น ให้อัปโหลดรูปโปรไฟล์ แต่เช็คแค่นามสกุล ผู้โจมตีจึงส่งไฟล์ .php หรือ .phtml ที่ปลอมเป็นรูปเข้ามาได้
  • ปลั๊กอินหรือธีมที่ไม่อัปเดต ช่องโหว่ของปลั๊กอิน WordPress เป็นทางเข้าที่พบบ่อย เพราะเว็บจำนวนมากติดตั้งแล้วไม่เคยอัปเดตอีกเลย
  • ตัว PHP เองมีช่องโหว่ ตัวอย่างที่ชัดคือ CVE-2024-4577 ช่องโหว่ของ PHP-CGI บน Windows (รวมถึง XAMPP ที่ตั้งค่าเริ่มต้น) ซึ่งถูกนำไปใช้โจมตีจริงไม่กี่วันหลังประกาศแพตช์ในเดือนมิถุนายน 2567 เพื่อรันโค้ดบนเครื่องเหยื่อและปล่อย ransomware
  • รหัสผ่านหลุด ทั้งรหัสหน้า admin ของเว็บ, FTP หรือแผงควบคุมโฮสติ้ง ผู้โจมตีที่ล็อกอินได้ก็อัปโหลดไฟล์ได้ตามปกติ

สัญญาณที่บอกว่าอาจมี web shell อยู่แล้ว

  • มีไฟล์ .php ในโฟลเดอร์ที่ควรมีแต่รูปหรือเอกสาร เช่น wp-content/uploads
  • ไฟล์ PHP เดิมของเว็บมีวันที่แก้ไขใหม่ โดยที่ทีมไม่ได้แก้อะไร
  • เจอโค้ดแปลก ๆ อย่าง eval(base64_decode( หรือสตริงยาว ๆ ที่อ่านไม่ออก
  • ใน access log มีคำขอ POST ไปยังไฟล์ที่ไม่ควรถูกเรียก หรือไฟล์ชื่อแปลกที่ไม่มีหน้าไหนลิงก์ไป
  • CPU สูงผิดปกติ เครื่องส่งอีเมลออกจำนวนมาก หรือหน้าเว็บ redirect ไปเว็บอื่นเฉพาะคนที่มาจาก Google

Cloudflare ช่วยป้องกันได้อย่างไร

Cloudflare ทำหน้าที่เป็นด่านหน้าที่ทุกคำขอต้องผ่านก่อนถึงเซิร์ฟเวอร์ จึงกรองการโจมตีได้หลายจังหวะ ตั้งแต่ตอนพยายามวางไฟล์ จนถึงตอนพยายามเรียกใช้ไฟล์ที่วางไว้แล้ว

1. WAF Managed Rules

ทุกแพ็กเกจของ Cloudflare รวมแพ็กเกจฟรี มี Free Managed Ruleset ที่ Cloudflare ดูแลและอัปเดตเอง ส่วนแพ็กเกจ Pro ขึ้นไปเปิด Cloudflare Managed Ruleset และ OWASP Core Ruleset ได้ ซึ่งตรวจรูปแบบการโจมตีอย่าง command injection, remote code execution และการแทรกโค้ด PHP ในคำขอ ช่วยตัดการโจมตีที่อาศัยช่องโหว่ที่รู้จักแล้วก่อนถึงเซิร์ฟเวอร์ ข้อแนะนำคือเริ่มจากโหมด Log ดูผลสักระยะ แล้วค่อยเปลี่ยนเป็น Block เพื่อไม่ให้บล็อกการใช้งานจริงโดยไม่ตั้งใจ

หน้า Security Events ของ Cloudflare แสดงคำขอที่ OWASP Core Ruleset ตรวจพบ พร้อมรายชื่อกฎที่ตรงและคะแนน
ตัวอย่างหน้า Security Events เมื่อ OWASP Core Ruleset จับคำขอที่น่าสงสัยได้ แต่ละกฎที่ตรงจะเพิ่มคะแนน เกินเกณฑ์จึงลงมือตาม action ที่ตั้งไว้ · ที่มา: Cloudflare Docs (CC BY 4.0) ปรับขนาดภาพ

2. Custom rules ปิดทางเรียกไฟล์ PHP ในที่ที่ไม่ควรมี

ข้อนี้ได้ผลมากที่สุดเมื่อเทียบกับความง่าย โฟลเดอร์อัปโหลดไม่ควรมีไฟล์ PHP ที่ต้องรันเลย จึงสร้าง custom rule ให้บล็อกคำขอแบบนี้ได้ เช่น

lower(http.request.uri.path) contains "/wp-content/uploads/" and lower(http.request.uri.path) contains ".php" แล้วเลือก Action เป็น Block

หน้าสร้าง custom rule ของ Cloudflare WAF มีช่อง Field, Operator, Value, Expression Preview และ Choose action
หน้าสร้าง custom rule เลือกเงื่อนไขทีละช่อง หรือกด Edit expression เพื่อวางนิพจน์ด้านบนได้ทันที · ที่มา: Cloudflare Docs (CC BY 4.0) ปรับขนาดภาพ

ถึงผู้โจมตีจะวางไฟล์ได้สำเร็จ ก็เรียกใช้ผ่านโดเมนไม่ได้ แนวเดียวกันนี้ใช้กับหน้าที่ไม่ควรเปิดให้ทุกคนเข้าถึง เช่น /wp-admin, /xmlrpc.php หรือ phpMyAdmin โดยอนุญาตเฉพาะ IP ของออฟฟิศ หรือให้ผ่าน Managed Challenge ก่อน แพ็กเกจฟรีสร้าง custom rule ได้ 5 กฎ Pro 20 กฎ และ Business 100 กฎ

3. Rate limiting และการจัดการบอท

การเดารหัสผ่านหน้า login และการสแกนหาไฟล์ที่มีช่องโหว่ เป็นงานของบอทที่ยิงคำขอจำนวนมาก การตั้ง rate limiting rule ที่หน้า login และ endpoint อัปโหลด ร่วมกับ Bot Fight Mode ช่วยลดโอกาสที่ผู้โจมตีจะหารหัสผ่านเจอหรือหาช่องโหว่ได้

4. ปิดหน้า admin ด้วย Cloudflare Access

Cloudflare Access (ส่วนหนึ่งของ Cloudflare Zero Trust) ให้ผู้ใช้ยืนยันตัวตนก่อนถึงหน้าเว็บ เช่น ใส่รหัส OTP ที่ส่งไปทางอีเมลขององค์กร ใช้ครอบหน้าแอดมินของเว็บหรือ phpMyAdmin ได้ ต่อให้ช่องโหว่ของหน้าแอดมินยังไม่ได้แพตช์ ผู้โจมตีภายนอกก็เข้าไม่ถึง

แผนภาพการทำงานของ Cloudflare Access: เบราว์เซอร์ต้องยืนยันตัวตนกับ identity provider ก่อน Access จึงส่งคำขอพร้อม JWT ไปยังเซิร์ฟเวอร์
Cloudflare Access ตรวจตัวตนและนโยบายก่อน คำขอที่ไม่ผ่านไม่ถึงเซิร์ฟเวอร์เลย · ที่มา: Cloudflare Docs (CC BY 4.0) ปรับขนาดภาพ

5. ปิดทางเข้าตรงไปที่เซิร์ฟเวอร์

Cloudflare ป้องกันได้เฉพาะคำขอที่ผ่าน Cloudflare ถ้า IP จริงของเซิร์ฟเวอร์หลุด ผู้โจมตีจะยิงตรงไปที่ IP นั้นและข้ามกฎทั้งหมดข้างต้น จึงต้องตั้งไฟร์วอลล์ของเซิร์ฟเวอร์ให้รับพอร์ต 80/443 เฉพาะจากช่วง IP ของ Cloudflare หรือใช้ Cloudflare Tunnel ซึ่งให้เซิร์ฟเวอร์เชื่อมออกไปหา Cloudflare เอง ไม่ต้องเปิดพอร์ตขาเข้าเลย และเปิด Authenticated Origin Pulls ถ้าต้องการให้เซิร์ฟเวอร์ตรวจได้ว่าคำขอมาจาก Cloudflare จริง

แผนภาพ Cloudflare Tunnel: คำขอจากเบราว์เซอร์ผ่าน Cloudflare edge แล้วส่งต่อผ่าน cloudflared ที่รันบนเซิร์ฟเวอร์ไปยังบริการภายใน
Cloudflare Tunnel: cloudflared บนเซิร์ฟเวอร์เป็นฝ่ายเชื่อมออกไปหา Cloudflare คำขอจากผู้ใช้จึงเข้าได้ทางเดียวคือผ่าน Cloudflare · ที่มา: Cloudflare Docs (CC BY 4.0) ปรับขนาดภาพ

6. สแกนไฟล์ที่อัปโหลด (Enterprise)

Cloudflare มีความสามารถ Malicious uploads detection ที่สแกนไฟล์ในคำขออัปโหลดหามัลแวร์ แต่เป็น add-on ของแพ็กเกจ Enterprise และทำหน้าที่ตรวจจับอย่างเดียว ต้องเขียน custom rule เพิ่มเพื่อให้บล็อกไฟล์ที่ตรวจเจอ

สิ่งที่ Cloudflare ช่วยไม่ได้

  • ไม่เห็นข้างในเซิร์ฟเวอร์ ถ้ามี web shell วางไว้แล้วในตำแหน่งที่กฎไม่ได้ครอบ ผู้โจมตียังเรียกใช้ได้ Cloudflare ไม่ได้สแกนไฟล์บนเครื่องให้
  • ไม่ได้แพตช์ให้ managed rules ช่วยซื้อเวลา แต่ช่องโหว่ใน PHP หรือปลั๊กอินยังอยู่จนกว่าจะอัปเดต
  • ไม่ครอบช่องทางอื่น ผู้โจมตีที่ได้รหัส FTP, SSH หรือแผงควบคุมโฮสติ้ง วางไฟล์ได้โดยไม่ผ่าน Cloudflare เลย

สิ่งที่ควรทำที่ฝั่งเซิร์ฟเวอร์ควบคู่กัน

  • ปิดการรัน PHP ในโฟลเดอร์อัปโหลดที่เว็บเซิร์ฟเวอร์ (Apache หรือ Nginx) อีกชั้น ไม่พึ่ง Cloudflare อย่างเดียว
  • อัปเดต PHP ให้อยู่ในเวอร์ชันที่ยังได้รับแพตช์ความปลอดภัย และอัปเดตปลั๊กอิน ธีม และ CMS สม่ำเสมอ ลบปลั๊กอินที่ไม่ใช้
  • ปิดฟังก์ชันที่ web shell ใช้ผ่าน disable_functions ใน php.ini เช่น exec, shell_exec, system, passthru, proc_open และ popen ถ้าเว็บไม่ได้ใช้
  • ให้สิทธิ์ไฟล์น้อยที่สุด โปรแกรมเว็บเซิร์ฟเวอร์ไม่ควรเขียนทับไฟล์โค้ดของเว็บได้
  • ติดตั้งระบบเฝ้าดูการเปลี่ยนแปลงของไฟล์ (file integrity monitoring) เพื่อรู้ทันทีเมื่อมีไฟล์ PHP ใหม่หรือถูกแก้
  • สำรองข้อมูลตามหลัก 3-2-1 และเก็บสำเนาที่แก้ไขไม่ได้ เพื่อกู้เว็บกลับมาเป็นฉบับที่สะอาดได้

ถ้าเจอ web shell แล้วต้องทำอย่างไร

อย่าแค่ลบไฟล์แล้วจบ เพราะผู้โจมตีมักวางไว้หลายจุด และทางที่ใช้เข้ามายังเปิดอยู่ ลำดับที่ควรทำคือ แยกเว็บออกหรือขึ้นหน้าปิดปรับปรุงก่อน เก็บ log และสำเนาไฟล์ไว้ตรวจ หาว่าเข้ามาทางไหน ตรวจหาไฟล์และบัญชีผู้ใช้แปลกปลอมอื่น ๆ เปลี่ยนรหัสผ่านและกุญแจทุกตัวที่เกี่ยวข้อง กู้เว็บจาก backup ที่แน่ใจว่าสะอาด แล้วแพตช์จุดที่เป็นทางเข้าก่อนเปิดเว็บกลับมา

สรุป

Cloudflare เป็นด่านหน้าที่คุ้มค่า โดยเฉพาะ managed rules, custom rule ที่บล็อกไฟล์ PHP ในโฟลเดอร์อัปโหลด และการปิดหน้าแอดมินด้วย Access ซึ่งตั้งค่าได้ในเวลาไม่นาน แต่ต้องใช้คู่กับการปิดทางเข้าตรงไปที่เซิร์ฟเวอร์ การอัปเดตซอฟต์แวร์ และการตั้งค่าฝั่งเซิร์ฟเวอร์ ถึงจะป้องกัน web shell ได้ครบทั้งทางเข้าและทางใช้งาน