ระวัง: ฟังก์ชัน PHP และ WordPress ที่ทำให้ไซต์ของคุณไม่ปลอดภัย
เผยแพร่แล้ว: 2022-03-10ความปลอดภัยของเว็บไซต์ WordPress (หรืออื่น ๆ ) เป็นปัญหาหลายแง่มุม ขั้นตอนที่สำคัญที่สุดที่ทุกคนสามารถทำได้เพื่อให้แน่ใจว่าไซต์มีความปลอดภัยคือ พึงระลึกไว้เสมอว่าไม่มีกระบวนการหรือวิธีการเดียวเพียงพอที่จะทำให้แน่ใจว่าจะไม่มีอะไรเลวร้ายเกิดขึ้น แต่มีบางสิ่งที่คุณสามารถทำได้เพื่อช่วย หนึ่งในนั้นคือการเฝ้าสังเกต ในโค้ดที่คุณเขียน และโค้ดจากผู้อื่นที่คุณปรับใช้ สำหรับฟังก์ชันที่อาจส่งผลด้านลบ ในบทความนี้ เราจะครอบคลุมทั้งหมด: ฟังก์ชันที่นักพัฒนา WordPress ควรคิดให้ชัดเจนก่อนใช้งาน
WordPress เองมีไลบรารีฟังก์ชันขนาดใหญ่ ซึ่งบางส่วนอาจเป็นอันตรายได้ นอกจากนั้น ยังมีฟังก์ชัน PHP มากมายที่นักพัฒนา WordPress (PHP) จะใช้ด้วยความถี่บางอย่างที่อาจเป็นอันตรายเมื่อใช้งาน
ฟังก์ชันทั้งหมดเหล่านี้มีการใช้งานที่ถูกต้องและปลอดภัย แต่ยังเป็นฟังก์ชันที่ช่วยให้โค้ดของคุณถูกนำไปใช้ในทางที่ผิดได้ง่าย เราจะครอบคลุมเนื้อหาที่น่าจะเป็นสาเหตุของช่องโหว่ด้านความปลอดภัยมากที่สุด และเหตุใดคุณจึงต้องคอยระวัง ก่อนอื่นเราจะเรียกใช้ฟังก์ชัน PHP ในโค้ดของคุณซึ่งผู้ไม่หวังดีสามารถใช้เพื่อความชั่วร้ายได้ จากนั้นจึงพูดถึงฟังก์ชัน PHP เฉพาะของ WordPress ที่อาจทำให้เกิดอาการปวดหัวได้เช่นกัน
ฟังก์ชั่น PHP ที่น่าจับตามอง
ดังที่เราได้กล่าวไปแล้วว่า PHP มีฟังก์ชันที่ใช้งานง่ายมากมาย หน้าที่บางอย่างขึ้นชื่อในเรื่องความสะดวกที่ผู้คนสามารถทำสิ่งเลวร้ายกับพวกเขาได้ ฟังก์ชันทั้งหมดนี้มีการใช้งานอย่างเหมาะสม แต่ควรระมัดระวังเกี่ยวกับวิธีใช้งานและวิธีใช้งานโค้ดอื่นๆ ที่คุณดึงเข้ามาในโปรเจ็กต์
เราจะถือว่าคุณมีคำพูดวิเศษแล้ว และการลงทะเบียน globals ถูกปิด หากเวอร์ชัน PHP ของคุณรองรับ มันถูกปิดโดยค่าเริ่มต้นใน PHP 5 ขึ้นไป แต่สามารถเปิดได้สำหรับเวอร์ชันที่ต่ำกว่า 5.4 มีโฮสต์ไม่กี่แห่งที่อนุญาต แต่ฉันไม่คิดว่าบทความด้านความปลอดภัยของ PHP จะสมบูรณ์อย่างแท้จริงหากไม่ได้รวมไว้
หรือการกล่าวถึงว่า PHP 5.6 เป็นเวอร์ชัน 5.x ล่าสุดที่ยังคงได้รับการสนับสนุนด้านความปลอดภัยอย่างต่อเนื่อง คุณควรอยู่กับมันอย่างน้อย และคุณควรมีแผนที่จะย้ายไปยัง PHP 7 ก่อนการสนับสนุน 5.6 จะสิ้นสุดในสิ้นปี 2018
หลังจากนั้น คุณจะมีฟังก์ชันเสี่ยงที่ต้องจัดการ เริ่มด้วย…
extract
เป็นข่าวร้าย โดยเฉพาะอย่างยิ่งใน $_POST
หรือคล้ายกัน
โชคดีที่การใช้ฟังก์ชัน extract
ของ PHP ไม่ได้ผลมากนัก แก่นของการใช้งานคือคุณสามารถเรียกใช้บนอาร์เรย์ของข้อมูล และคู่คีย์-ค่าจะกลายเป็นตัวแปรที่มีชีวิตในโค้ดของคุณ ดังนั้น
$arr = array( 'red' => 5 ); extract($arr); echo $red; // 5
จะทำงาน. สิ่งนี้เจ๋ง แต่ก็อันตรายเช่นกันหากคุณแยก $_GET
, $_POST
ฯลฯ ในกรณีเหล่านี้ คุณกำลังสร้างปัญหา register_globals
ขึ้นมาใหม่ด้วยตนเอง: ผู้โจมตีภายนอกสามารถเปลี่ยนค่าตัวแปรของคุณได้อย่างง่ายดายโดยการเพิ่มสตริงการสืบค้น หรือช่องแบบฟอร์ม ทางออกที่ดีที่สุดคือง่าย ๆ อย่าใช้ extract
หากคุณสร้างอาร์เรย์ที่คุณดึงข้อมูลออกมาเอง การใช้การ extract
ข้อมูลไม่ใช่ปัญหาด้านความปลอดภัยโดยเฉพาะ และในบางกรณีก็อาจมีประโยชน์ แต่การใช้งานทั้งหมดมีปัญหาในการสร้างความสับสนให้กับผู้อ่านในอนาคต การสร้างอาร์เรย์และการ extract
การโทรนั้นสร้างความสับสนมากกว่าการประกาศตัวแปรของคุณ ดังนั้น เราขอแนะนำให้คุณประกาศด้วยตนเองแทน เว้นแต่จะเป็นไปไม่ได้เลย
หากคุณต้องใช้การแตกข้อมูลในการป้อนข้อมูลของผู้ใช้ คุณควรใช้แฟ EXTR_SKIP
extract
สิ่งนี้ยังคงมีปัญหาความสับสน แต่จะกำจัดความเป็นไปได้ที่ค่าที่ตั้งไว้ล่วงหน้าของคุณสามารถเปลี่ยนแปลงได้โดยบุคคลภายนอกที่ประสงค์ร้ายผ่านสตริงการสืบค้นอย่างง่ายหรือการแก้ไขแบบฟอร์มบนเว็บ (เพราะมัน “ข้าม” ตั้งค่าไว้แล้ว)
“ eval
Is Evil” เพราะรหัสตามอำเภอใจนั้นน่ากลัว
eval
ใน PHP และภาษาอื่น ๆ ที่ดำเนินการอยู่เสมอในรายการเช่นนี้ และด้วยเหตุผลที่ดี เอกสารอย่างเป็นทางการของ PHP บน PHP.net บอกว่าค่อนข้างตรงไปตรงมา:
ข้อควรระวัง : การสร้างภาษา eval() นั้นอันตรายมาก เนื่องจากอนุญาตให้เรียกใช้โค้ด PHP ได้ตามอำเภอใจ การใช้งานจึงหมดกำลังใจ หากคุณได้ตรวจสอบอย่างรอบคอบแล้วว่าไม่มีทางเลือกอื่นนอกจากการใช้โครงสร้างนี้ ให้ความสนใจเป็นพิเศษที่จะไม่ส่งข้อมูลใดๆ ที่ผู้ใช้ให้เข้าไปโดยไม่ได้ตรวจสอบความถูกต้องก่อนล่วงหน้าอย่างเหมาะสม
eval
อนุญาตให้รันสตริงใดก็ได้ในโปรแกรมของคุณราวกับว่าเป็นโค้ด PHP นั่นหมายความว่ามีประโยชน์สำหรับ "meta-programming" ซึ่งคุณกำลังสร้างโปรแกรมที่สามารถสร้างโปรแกรมได้เอง มันยังอันตรายจริงๆ เพราะหากคุณเคยอนุญาตให้ส่งแหล่งข้อมูลที่กำหนดเอง (เช่น กล่องข้อความบนหน้าเว็บ) ไปยังเครื่องมือประเมินสตริงการประเมินของคุณทันที คุณก็ทำให้ผู้โจมตีที่ eval
สามารถทำ อะไรก็ได้ที่ PHP สามารถทำได้ ทำบนเซิร์ฟเวอร์ของคุณ ซึ่งรวมถึง การเชื่อมต่อกับฐานข้อมูล การลบไฟล์ และทุกๆ อย่างที่คนอื่นสามารถทำได้เมื่อ SSHed เข้าสู่เครื่อง นี้ไม่ดี.
หากคุณต้องใช้ eval
ในโปรแกรมของคุณ คุณควรพยายามตรวจสอบให้แน่ใจว่าคุณไม่ได้อนุญาตให้มีการส่งข้อมูลของผู้ใช้ตามอำเภอใจเข้ามา และหากคุณ ต้อง อนุญาตให้ผู้ใช้ตามอำเภอใจเข้าถึงอินพุตเพื่อ eval
ให้จำกัดสิ่งที่พวกเขาสามารถทำได้ผ่านบัญชีดำของคำสั่งที่คุณไม่ปล่อยให้มันทำงาน หรือ (ดีกว่า แต่ยากกว่ามากที่จะนำไปใช้) รายการที่อนุญาตพิเศษซึ่งเป็นเพียงคำสั่งของคุณ ถือว่าปลอดภัย ยังดีกว่า อนุญาตให้มีการเปลี่ยนแปลงพารามิเตอร์เฉพาะจำนวนเล็กน้อย เช่น เฉพาะจำนวนเต็มที่ตรวจสอบความถูกต้อง
แต่โปรดจำไว้เสมอว่าบรรทัดนี้จาก Rasmus Lerdorf ผู้ก่อตั้ง PHP:
"ถ้า `eval()` คือคำตอบ แสดงว่าคุณกำลังถามคำถามผิดอยู่"
รูปแบบต่างๆ บน eval
นอกจาก eval
ที่เป็นที่รู้จักแล้ว ยังมีวิธีอื่นๆ อีกหลายอย่างที่ PHP ได้สนับสนุนในอดีตเป็นโค้ดที่ได้รับการประเมินสตริงเป็นโค้ด สองตัวที่มีความเกี่ยวข้องมากที่สุดสำหรับนักพัฒนา WordPress คือ preg_replace
ด้วยตัวแก้ไข /e
และ create_function
แต่ละอันทำงานแตกต่างกันเล็กน้อย และ preg_replace
หลังจาก PHP 5.5.0 จะไม่ทำงานเลย (PHP 5.5 และต่ำกว่าจะไม่ได้รับการอัปเดตความปลอดภัยอย่างเป็นทางการอีกต่อไป ดังนั้นจึงเป็นการดีที่สุดที่จะไม่ใช้)
/e
ตัวดัดแปลงใน Regex ยังเป็น "ความชั่วร้าย"
หากคุณใช้ PHP 5.4.x หรือต่ำกว่า คุณจะต้องคอยจับตาดูการเรียก PHP preg_replace
ที่ลงท้ายด้วย e ที่สามารถมีลักษณะดังนี้:
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
PHP มีหลายวิธีในการ "ป้องกัน" ในนิพจน์ทั่วไปของคุณ แต่สิ่งสำคัญที่คุณต้องการให้ระวังคือ "e" เป็นอักขระตัวสุดท้ายในอาร์กิวเมนต์แรกของ preg_replace
หากมี แสดงว่าคุณกำลังส่งเซ็กเมนต์ที่พบทั้งหมดเป็นอาร์กิวเมนต์ไปยัง PHP แบบอินไลน์ของคุณ แล้ว eval
ประเมินส่วนนั้น สิ่งนี้มีปัญหาเดียวกันกับ eval
หากคุณปล่อยให้ผู้ใช้ป้อนเข้าสู่ฟังก์ชันของคุณ โค้ดตัวอย่างสามารถแทนที่โดยใช้ preg_replace_callback
แทน ข้อดีของสิ่งนี้คือคุณได้เขียนฟังก์ชันของคุณ ดังนั้นผู้โจมตีจะเปลี่ยนสิ่งที่ได้รับการประเมินได้ยากขึ้น ดังนั้นคุณจะเขียนข้างต้นเป็น:
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
การอนุญาตให้ผู้ใช้ป้อนข้อมูล create_function
s ก็ไม่ดีเช่นกัน...
PHP ยังมีฟังก์ชัน create_function
สิ่งนี้เลิกใช้แล้วใน PHP 7.2 แต่คล้ายกับ eval
มากและมีข้อเสียพื้นฐานเหมือนกัน: อนุญาตให้เปลี่ยนสตริง (อาร์กิวเมนต์ที่สอง) เป็น PHP ที่ปฏิบัติการได้ และมีความเสี่ยงเหมือนกัน: ง่ายเกินไปสำหรับคุณที่จะให้โปรแกรมแคร็กเกอร์อัจฉริยะทำ สิ่งใดๆ บนเซิร์ฟเวอร์ของคุณโดยไม่ได้ตั้งใจถ้าคุณไม่ระวัง
ข้อนี้ หากคุณใช้ PHP ที่สูงกว่า 5.3 จะแก้ไขได้ง่ายกว่า preg_replace
คุณสามารถสร้างฟังก์ชันที่ไม่ระบุตัวตนของคุณเองได้โดยไม่ต้องใช้สตริงเป็นตัวกลาง สิ่งนี้ปลอดภัยกว่าและอ่านง่ายกว่า อย่างน้อยก็ในสายตาของฉัน
assert
ยัง eval
-Like
assert
ไม่ใช่ฟังก์ชันที่ฉันเห็นนักพัฒนา PHP จำนวนมากใช้ใน WordPress หรือภายนอก จุดประสงค์คือเพื่อยืนยันเงื่อนไขเบื้องต้นสำหรับโค้ดของคุณ แต่ยังสนับสนุนการดำเนินการประเภท eval
ด้วย ด้วยเหตุผลดังกล่าว คุณจึงควรระมัดระวังเช่นเดียวกับคุณของ eval
การยืนยันแบบอิงสตริง (หัวใจสำคัญของเหตุนี้จึงแย่) ก็เลิกใช้แล้วใน PHP 7.2 ซึ่งหมายความว่าไม่น่าเป็นห่วงในอนาคต
การรวมไฟล์ตัวแปรเป็นวิธีการที่อาจอนุญาตให้ดำเนินการ PHP ที่ไม่สามารถควบคุมได้
เราได้สำรวจมาอย่างดีแล้วว่าทำไม eval
ถึงไม่ดี แต่บางอย่างเช่น include
หรือ require($filename'.php')
สามารถโดยเฉพาะอย่างยิ่งเมื่อ $filename
ถูกตั้งค่าจากค่าที่ผู้ใช้ควบคุมได้ อาจเป็นข่าวร้ายในทำนองเดียวกัน เหตุผลแตกต่างไปจาก eval
อย่างสิ้นเชิง ชื่อไฟล์ตัวแปรมักใช้สำหรับสิ่งต่างๆ เช่น การกำหนดเส้นทาง URL สู่ไฟล์อย่างง่ายในแอป PHP ที่ไม่ใช่ WordPress แต่คุณอาจเห็นพวกเขาใช้ใน WordPress เช่นกัน
หัวใจของปัญหาที่ว่าเมื่อคุณ include
หรือ require
(หรือ include_once
หรือ require_once
) คุณกำลังทำให้สคริปต์ของคุณรันไฟล์ที่รวมอยู่ มันไม่มากก็น้อยในการ eval
แนวคิดของไฟล์นั้น แม้ว่าเราจะไม่ค่อยคิดอย่างนั้นก็ตาม
หากคุณได้เขียนไฟล์ทั้งหมดที่ตัวแปรของคุณ include
ไว้อาจดึงเข้ามา และได้พิจารณาว่าจะเกิดอะไรขึ้นเมื่อมันเกิดขึ้น คุณก็ไม่เป็นไร แต่เป็นข่าวร้ายหากคุณไม่ได้พิจารณาถึงสิ่งที่ include
ing password.php
หรือ wp-config.php
ไว้ด้วย นอกจากนี้ยังเป็นข่าวร้ายหากมีใครสามารถเพิ่มไฟล์ที่เป็นอันตรายและดำเนินการ include
ของคุณ (แม้ว่า ณ จุดนั้นคุณอาจมีปัญหาใหญ่กว่า)
วิธีแก้ปัญหานี้ไม่ยากเกินไป: ฮาร์ดโค้ดรวมเวลาที่คุณสามารถทำได้ เมื่อทำไม่ได้ ให้เพิ่มบัญชีขาว (ดีกว่า) หรือบัญชีดำของไฟล์ที่จะรวมได้ หากไฟล์ในรายการที่อนุญาตพิเศษ (นั่นคือ: คุณได้ตรวจสอบแล้วว่าทำอะไรได้บ้างเมื่อเพิ่มเข้าไป) คุณจะรู้ว่าคุณปลอดภัย หากไม่ได้อยู่ในรายการที่อนุญาต สคริปต์ของคุณจะไม่รวมไว้ ด้วยการปรับแต่งง่ายๆ นั้น คุณจะค่อนข้างปลอดภัย รายการที่อนุญาตพิเศษจะมีลักษณะดังนี้:
$white_list = ['db.php', filter.php', 'condense.php'] If (in_array($white_list, $file_to_include)) { include($file_to_include); }
ไม่เคยผ่านการป้อนข้อมูลของผู้ใช้ไปยัง shell_exec
และ Variants
นี่เป็นเรื่องใหญ่ shell_exec
, system
, exec
และ backticks ใน PHP อนุญาตให้โค้ดที่คุณกำลังเรียกใช้เพื่อพูดคุยกับเชลล์พื้นฐาน (โดยปกติคือ Unix) สิ่งนี้คล้ายกับสิ่งที่ทำให้ eval
อันตรายแต่เพิ่มเป็นสองเท่า เพิ่มเป็นสองเท่าเพราะถ้าคุณปล่อยให้ผู้ใช้ป้อนข้อมูลผ่านที่นี่อย่างไม่ระมัดระวัง ผู้โจมตีจะไม่ถูกผูกมัดด้วยข้อจำกัดของ PHP
ความสามารถในการเรียกใช้คำสั่งเชลล์จาก PHP นั้นมีประโยชน์ มาก ในฐานะนักพัฒนา แต่ถ้าคุณปล่อยให้ผู้ใช้ป้อนข้อมูลเข้าไป พวกเขาสามารถได้รับพลังอันตรายมากมายแอบแฝง ดังนั้นฉันจะบอกว่าการป้อนข้อมูลของผู้ใช้ ไม่ ควรส่งผ่านไปยังฟังก์ชัน shell_exec
-type
วิธีที่ดีที่สุดที่ฉันคิดว่าจะจัดการกับสถานการณ์ประเภทนี้ หากคุณถูกล่อลวงให้ใช้งาน คือการให้ผู้ใช้เข้าถึงชุดคำสั่งเชลล์ที่กำหนดไว้ล่วงหน้าที่ปลอดภัยซึ่งเป็นที่รู้จัก ที่ อาจ จะสามารถรักษาความปลอดภัยได้ แต่ถึงอย่างนั้นฉันก็จะเตือนคุณให้ระวังให้มาก
ระวัง unserialize
; มันรันโค้ดโดยอัตโนมัติ
การดำเนินการหลักของการเรียก serialize
บนอ็อบเจ็กต์ PHP ที่มีชีวิต จัดเก็บข้อมูลนั้นไว้ที่ใดที่หนึ่ง แล้วใช้ค่าที่เก็บไว้นั้นในภายหลังเพื่อ unserialize
ออบเจกต์นั้นให้กลับมามีชีวิตอีกครั้งนั้นยอดเยี่ยม นอกจากนี้ยังเป็นเรื่องปกติที่สมเหตุสมผล แต่อาจมีความเสี่ยง ทำไมต้องเสี่ยง? หากอินพุตของการโทรที่ไม่ unserialize
นั้นไม่ปลอดภัยอย่างสมบูรณ์ (เช่น มันถูกเก็บไว้เป็นคุกกี้แทนที่จะเป็นในฐานข้อมูลของคุณ…) ผู้โจมตีสามารถเปลี่ยนสถานะภายในของออบเจกต์ของคุณในลักษณะที่ทำให้การเรียกที่ไม่ unserialize
ทำสิ่งที่ไม่ดี
การหาประโยชน์นี้มีความลึกลับและมีโอกาสน้อยที่จะสังเกตเห็นมากกว่าปัญหาการ eval
แต่ถ้าคุณใช้คุกกี้เป็นกลไกการจัดเก็บข้อมูลสำหรับข้อมูลที่ต่อเนื่องกัน อย่าใช้การทำให้เป็น serialize
สำหรับข้อมูลนั้น ใช้บางอย่างเช่น json_encode
และ json_decode
ด้วยสอง PHP นั้นจะไม่รันโค้ดใด ๆ โดยอัตโนมัติ
ช่องโหว่หลักที่นี่คือเมื่อ PHP unserialize
sa string ในคลาส มันจะเรียกเมธอด magic __wakeup
ในคลาสนั้น หากการป้อนข้อมูลของผู้ใช้ที่ไม่ผ่านการตรวจสอบได้รับอนุญาตให้ยกเลิกการ unserialized
ลไลซ์ การเรียกฐานข้อมูลหรือการลบไฟล์ในวิธี __wakeup
อาจถูกชี้ไปที่ตำแหน่งที่เป็นอันตรายหรือไม่ต้องการ
unserialize
แตกต่างจากช่องโหว่ของ eval
เนื่องจากต้องใช้เมธอดเวทย์มนตร์กับอ็อบเจ็กต์ แทนที่จะสร้างรหัสของตนเอง ผู้โจมตีถูกบังคับให้ใช้วิธีการที่คุณเขียนไว้แล้วในทางที่ผิดบนวัตถุ ทั้งเมธอดเวทย์มนตร์ __destruct
และ __toString
บนออบเจ็กต์ก็มีความเสี่ยงเช่นกัน ดังที่หน้า OWASP Wiki นี้อธิบายไว้
โดยทั่วไป คุณจะไม่เป็นไรถ้าคุณไม่ใช้เมธอด __destruct
__wakeup
__toString
ในชั้นเรียนของคุณ แต่เนื่องจากคุณอาจเห็นใครบางคนเพิ่มพวกเขาในชั้นเรียน จึงเป็นความคิดที่ดีที่จะไม่ปล่อยให้ผู้ใช้อยู่ใกล้การโทรของคุณเพื่อ serialize
ลไลซ์และยกเลิกการซีเรีย unserialize
และส่งข้อมูลสาธารณะทั้งหมดสำหรับการใช้งานประเภทนั้น แม้ว่าจะมีบางอย่างเช่น JSON ( json_encode
และ json_decode
) ที่นั่น ไม่มีการเรียกใช้โค้ดโดยอัตโนมัติ
การดึง URL ด้วย file_get_contents
มีความเสี่ยง
แนวทางปฏิบัติทั่วไปในการเขียนโค้ด PHP อย่างรวดเร็วซึ่งต้องเรียก URL ภายนอกคือการเข้าถึง file_get_contents
รวดเร็ว ง่าย แต่ไม่ปลอดภัยอย่างยิ่ง
ปัญหาเกี่ยวกับ file_get_contents
เป็นเรื่องเล็กน้อย แต่เป็นเรื่องปกติที่บางครั้งโฮสต์จะกำหนดค่า PHP ไม่ให้เข้าถึง URL ภายนอกได้ สิ่งนี้มีขึ้นเพื่อปกป้องคุณ
ปัญหาที่นี่คือ file_get_contents
จะดึงเพจระยะไกลให้คุณ แต่เมื่อทำเช่นนั้น จะไม่ตรวจสอบความสมบูรณ์ของการเชื่อมต่อโปรโตคอล HTTPS หมายความว่าสคริปต์ของคุณอาจตกเป็นเหยื่อของการโจมตีแบบคนกลาง ซึ่งจะทำให้ผู้โจมตีใส่สิ่งที่พวกเขาต้องการลงในผลลัพธ์หน้า file_get_contents
ของคุณได้
นี่เป็นการโจมตีที่ลึกลับกว่า แต่เพื่อป้องกันเมื่อฉันเขียน PHP ที่ทันสมัย (อิงตามผู้แต่ง) ฉันมักจะใช้ Guzzle เพื่อห่อ cURL API ที่ปลอดภัยกว่าเสมอ ใน WordPress นั้นง่ายยิ่งขึ้น: ใช้ wp_remote_get
มันทำงานอย่างสม่ำเสมอมากกว่า file_get_contents
และค่าเริ่มต้นจะเป็นการยืนยันการเชื่อมต่อ SSL (คุณสามารถปิดได้ แต่ เอ่อ อาจจะไม่…) ยังดีกว่า แต่น่ารำคาญกว่าเล็กน้อยที่จะพูดถึงคือ wp_safe_remote_get
ฯลฯ สิ่งเหล่านี้ทำงานเหมือนกับฟังก์ชั่นที่ไม่มี safe_
ในชื่อ แต่พวกเขาจะทำ ตรวจสอบให้แน่ใจว่าการเปลี่ยนเส้นทางและส่งต่อที่ไม่ปลอดภัยจะไม่เกิดขึ้นระหว่างทาง
อย่าเชื่อถือการตรวจสอบ URL จาก filter_var
อันนี้ค่อนข้างคลุมเครือ โปรดให้ Chris Weigman อธิบายในการบรรยาย WordCamp นี้ โดยทั่วไป filter_var
ของ PHP เป็นวิธีที่ยอดเยี่ยมในการตรวจสอบหรือล้างข้อมูล (แต่อย่าสับสนว่าคุณกำลังพยายามทำอะไรอยู่…)
ปัญหาที่นี่ค่อนข้างเฉพาะเจาะจง: หากคุณกำลังพยายามใช้เพื่อให้แน่ใจว่า URL ปลอดภัย filter_var
จะไม่ตรวจสอบโปรโตคอล นั่นไม่ใช่ปัญหาบ่อยนัก แต่ถ้าคุณส่งข้อมูลของผู้ใช้ไปยังวิธีนี้เพื่อตรวจสอบความถูกต้อง และใช้ FILTER_VALIDATE_URL
บางอย่างเช่น javascript://comment%0aalert(1)
จะผ่านพ้นไป นั่นคือ นี่อาจเป็นเวกเตอร์ที่ดีมากสำหรับการโจมตี XSS พื้นฐานในที่ที่คุณคาดไม่ถึง
สำหรับการตรวจสอบความถูกต้องของ URL ฟังก์ชัน esc_url
ของ WordPress จะมีผลเช่นเดียวกัน แต่จะอนุญาตเฉพาะผ่านโปรโตคอลที่อนุญาตเท่านั้น javascript
ไม่อยู่ในรายการเริ่มต้น ดังนั้นมันจะช่วยให้คุณปลอดภัย อย่างไรก็ตาม ไม่เหมือนกับ filter_var
มันจะส่งคืนสตริงว่าง (ไม่ใช่เท็จ) สำหรับโปรโตคอลที่ไม่อนุญาตที่ส่งผ่านไปยังมัน
ฟังก์ชันเฉพาะของ WordPress เพื่อจับตาดู
นอกเหนือจากฟังก์ชัน core-PHP ที่อาจมีความเสี่ยงแล้ว ยังมีบางฟังก์ชันเฉพาะของ WordPress ที่อาจดูเหมือนเป็น Gotcha เล็กน้อย บางส่วนมีความคล้ายคลึงกันมากกับความหลากหลายของฟังก์ชันอันตรายที่ระบุไว้ข้างต้น บางอย่างแตกต่างกันเล็กน้อย
WordPress Unserializes ด้วย maybe_unserialize
อันนี้อาจชัดเจนถ้าคุณอ่านข้างต้น ใน WordPress มีฟังก์ชันที่เรียกว่า maybe_unserialize
และอย่างที่คุณเดา มันจะ unserializes สิ่งที่ส่งผ่านไปหากจำเป็น
ไม่มีช่องโหว่ใหม่ใด ๆ ที่แนะนำนี้ ปัญหาก็คือว่าเช่นเดียวกับฟังก์ชัน unserialize
หลัก ช่องโหว่นี้สามารถทำให้วัตถุที่มีช่องโหว่ถูกใช้ประโยชน์ได้เมื่อไม่มีการจัดลำดับ
is_admin
ไม่ตอบหากผู้ใช้เป็นผู้ดูแลระบบ!
อันนี้ค่อนข้างเรียบง่าย แต่ฟังก์ชั่นนั้นคลุมเครือในชื่อ ดังนั้นจึงมีแนวโน้มที่จะสร้างความสับสนให้กับผู้คนหรือถูกมองข้ามหากคุณรีบร้อน คุณควรตรวจสอบอยู่เสมอว่าผู้ใช้ที่พยายามดำเนินการใดๆ ใน WordPress มีสิทธิ์และสิทธิพิเศษที่จำเป็นในการดำเนินการดังกล่าว เพื่อที่จะทำอย่างนั้น คุณ ควร ใช้ฟังก์ชัน current_user_can
แต่คุณอาจคิดผิดว่า is_admin
จะบอกคุณว่าผู้ใช้ปัจจุบันเป็นบัญชีระดับผู้ดูแลระบบหรือไม่ และด้วยเหตุนี้จึงควรตั้งค่าตัวเลือกที่ปลั๊กอินของคุณใช้ นี่เป็นความผิดพลาด is_admin
ทำอะไรใน WordPress แทนที่จะบอกคุณว่าการโหลดหน้าปัจจุบันอยู่ที่ฝ่ายดูแลระบบของไซต์หรือไม่ (เทียบกับด้านหน้า) ดังนั้นผู้ใช้ทุกคนที่สามารถเข้าถึงหน้าการดูแลระบบ (เช่น “แดชบอร์ด”) จะสามารถผ่านการตรวจสอบนี้ได้ ตราบใดที่คุณจำได้ว่า is_admin
นั้นเกี่ยวกับประเภทของเพจ ไม่ใช่ผู้ใช้ปัจจุบัน คุณก็ไม่เป็นไร
add_query_arg()
ไม่ฆ่าเชื้อ URLs
สิ่งนี้ไม่ธรรมดา แต่มีคลื่นลูกใหญ่ของการอัปเดตในระบบนิเวศของ WordPress เมื่อไม่กี่ปีที่ผ่านมาเนื่องจากเอกสารสาธารณะเกี่ยวกับฟังก์ชันนี้ไม่ถูกต้อง ปัญหาหลักคือฟังก์ชัน add_query_arg
(และฟังก์ชัน remove_query_arg
ผกผัน) จะไม่ล้าง URL ของไซต์โดยอัตโนมัติหาก URL ไม่ได้ถูกส่งผ่านไป และผู้คนก็คิดว่ามันเป็นเช่นนั้น ปลั๊กอินจำนวนมากถูกโคเด็กซ์เข้าใจผิด และกำลังใช้สิ่งนี้อย่างไม่ปลอดภัยด้วยเหตุนี้
สิ่งสำคัญที่พวกเขาต้องทำแตกต่างออกไป: ฆ่าเชื้อผลลัพธ์ของการเรียกใช้ฟังก์ชันนี้ก่อนใช้งาน หากคุณทำเช่นนั้น คุณจะปลอดภัยจากการโจมตี XSS อย่างที่คุณคิดว่าเป็น นั่นดูเหมือน:
echo esc_url( add_query_arg( 'foo', 'bar' ) );
$wpdb->query()
เปิดให้ SQL Injection Attack
หากคุณรู้เกี่ยวกับการฉีด SQL นี่อาจดูงี่เง่า แม้จะไม่จำเป็นในรายการก็ตาม เพราะสิ่งนี้คือวิธีใดก็ตามที่คุณเข้าถึงฐานข้อมูล (เช่น ใช้ไดรเวอร์ฐานข้อมูล mysqli
หรือ PDO ของ PHP) เพื่อสร้างการสืบค้นฐานข้อมูลที่อนุญาตให้มีการโจมตีด้วยการฉีด SQL
เหตุผลที่ฉันเรียก $wpdb->query
โดยเฉพาะคือวิธีการอื่น (เช่น insert
, delete
ฯลฯ ) บน $wpdb
จะดูแลการโจมตีแบบฉีดให้กับคุณ นอกจากนี้ หากคุณคุ้นเคยกับการสืบค้นฐานข้อมูล WordPress พื้นฐานด้วย WP_Query
หรือที่คล้ายกัน คุณไม่จำเป็นต้องพิจารณาการฉีด SQL นี่คือเหตุผลที่ฉันเรียกมันออกมา: เพื่อให้แน่ใจว่าคุณเข้าใจการโจมตีแบบฉีดบนฐานข้อมูลนั้นเป็นไปได้เมื่อคุณพยายามใช้ $wpdb
เพื่อสร้างแบบสอบถามของคุณเองเป็นครั้งแรก
จะทำอย่างไร? ใช้ $wpdb->prepare()
จากนั้น $wpdb->query()
คุณจะต้องแน่ใจว่าได้เตรียมตัวก่อนวิธีการ "get" อื่นๆ เช่น $wpdb
เช่น get_row()
และ get_var()
มิฉะนั้น Bobby Tables อาจเข้าใจคุณได้
esc_sql
ไม่ปกป้องคุณจากการฉีด SQL เช่นกัน
สำหรับนักพัฒนา WordPress ส่วนใหญ่ ฉันคิดว่า esc_sql
ไม่ได้ลงทะเบียนด้วยความหมายใดๆ แต่ควรเป็นเช่นนั้น ดังที่เราได้กล่าวไปแล้ว คุณควรใช้ wpdb->prepare()
ก่อนที่คุณจะทำการสืบค้นฐานข้อมูล นี้จะช่วยให้คุณปลอดภัย แต่น่าดึงดูดและเข้าใจได้ว่านักพัฒนาอาจเข้าถึง esc_sql
แทน และพวกเขาอาจจะคาดหวังว่ามันจะปลอดภัย
ปัญหาคือ esc_sql
ไม่มีการป้องกันการฉีด SQL ที่แข็งแกร่ง เป็นฟังก์ชัน add_slashes
ของ PHP เวอร์ชันที่ได้รับการยกย่องจริงๆ ซึ่งคุณไม่แนะนำให้ใช้เพื่อปกป้องฐานข้อมูลของคุณมานานหลายปี
มีอะไรให้ทำอีกมาก แต่นี่คือการเริ่มต้นครั้งยิ่งใหญ่
การรักษาความปลอดภัยมีอะไรมากกว่าแค่การมองหาฟังก์ชันในโค้ดของคุณที่ผู้โจมตีสามารถใช้ในทางที่ผิด ตัวอย่างเช่น เราไม่ได้พูดคุยกันในเชิงลึกถึงความจำเป็นในการตรวจสอบและล้างข้อมูลทั้งหมดที่คุณได้รับจากผู้ใช้และหลีกเลี่ยงข้อมูลนั้นก่อนที่คุณจะใส่ลงในหน้าเว็บ (แม้ว่าฉันเพิ่งเผยแพร่บทความในหัวข้อนั้น “การปกป้อง WordPress ของคุณ” Site Against A Cross-Site Scripting Attack”) แต่คุณสามารถและควรใช้สิ่งนี้เป็นส่วนหนึ่งของกลยุทธ์ความปลอดภัยที่กว้างขึ้น
การรักษารายการฟังก์ชันที่ง่ายต่อการใช้งานในทางที่ผิดไว้ข้างคุณในขณะที่คุณทำการตรวจสอบขั้นสุดท้ายก่อนที่จะปรับใช้ปลั๊กอินใหม่ใน WordPress เป็นความคิดที่ดี แต่คุณจะต้องแน่ใจว่าคุณไว้วางใจในความปลอดภัยของโฮสติ้งของคุณ ตรวจสอบให้แน่ใจว่าผู้ใช้ของคุณมีรหัสผ่านที่ดี และอื่นๆ อีกมากมาย
สิ่งสำคัญที่ต้องจำเกี่ยวกับความปลอดภัยคือลิงก์ที่อ่อนแอที่สุดของคุณคือลิงก์ที่สำคัญ แนวปฏิบัติที่ดีในพื้นที่หนึ่งหนุนจุดอ่อนที่เป็นไปได้ในที่อื่น แต่พวกเขาไม่สามารถแก้ไขได้ทั้งหมด แต่จงระวังฟังก์ชั่นเหล่านี้ให้ดี แล้วคุณจะได้ประโยชน์สูงสุดจากมัน