ระวัง: ฟังก์ชัน PHP และ WordPress ที่ทำให้ไซต์ของคุณไม่ปลอดภัย

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างย่อ ↬ ก่อนปรับใช้ปลั๊กอินใหม่ใน WordPress คุณควรเก็บรายการฟังก์ชันที่ง่ายต่อการใช้งานในทางที่ผิดไว้เคียงข้างคุณ มาดูฟังก์ชันบางอย่างที่คุณสามารถใช้ได้และควรใช้เป็นส่วนหนึ่งของกลยุทธ์ด้านความปลอดภัยที่กว้างขึ้นกันดีกว่า

ความปลอดภัยของเว็บไซต์ 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 เป็นความคิดที่ดี แต่คุณจะต้องแน่ใจว่าคุณไว้วางใจในความปลอดภัยของโฮสติ้งของคุณ ตรวจสอบให้แน่ใจว่าผู้ใช้ของคุณมีรหัสผ่านที่ดี และอื่นๆ อีกมากมาย

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