คำตอบ:
เมื่อพูดถึงการสืบค้นฐานข้อมูลให้พยายามใช้แบบสอบถามที่กำหนดพารามิเตอร์ไว้เสมอ mysqli
และPDO
ห้องสมุดสนับสนุนเรื่องนี้ ปลอดภัยกว่าการใช้ฟังก์ชัน Escape อย่างไม่มีที่สิ้นสุดเช่นmysql_real_escape_string
.
ใช่mysql_real_escape_string
เป็นเพียงฟังก์ชันหนีสตริงเท่านั้น มันไม่ใช่กระสุนวิเศษ สิ่งที่ต้องทำคือหลีกเลี่ยงอักขระอันตรายเพื่อให้สามารถใช้ในสตริงการสืบค้นเดียวได้อย่างปลอดภัย อย่างไรก็ตามหากคุณไม่ทำความสะอาดอินพุตของคุณล่วงหน้าคุณจะเสี่ยงต่อเวกเตอร์การโจมตีบางอย่าง
ลองนึกภาพ SQL ต่อไปนี้:
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
คุณควรจะเห็นว่าสิ่งนี้เสี่ยงต่อการถูกใช้ประโยชน์
ลองนึกภาพid
พารามิเตอร์ที่มีเวกเตอร์การโจมตีทั่วไป:
1 OR 1=1
ไม่มีตัวอักษรที่มีความเสี่ยงในการเข้ารหัสดังนั้นมันจะผ่านตัวกรองการหลบหนีโดยตรง ออกจากเรา:
SELECT fields FROM table WHERE id= 1 OR 1=1
ซึ่งเป็นเวกเตอร์การฉีด SQL ที่น่ารักและอนุญาตให้ผู้โจมตีส่งคืนแถวทั้งหมด หรือ
1 or is_admin=1 order by id limit 1
ซึ่งผลิต
SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
ซึ่งช่วยให้ผู้โจมตีสามารถส่งคืนรายละเอียดของผู้ดูแลระบบรายแรกในตัวอย่างสมมตินี้ได้อย่างสมบูรณ์
แม้ว่าฟังก์ชันเหล่านี้จะมีประโยชน์ แต่ต้องใช้ด้วยความระมัดระวัง คุณต้องแน่ใจว่าอินพุตของเว็บทั้งหมดได้รับการตรวจสอบความถูกต้องในระดับหนึ่ง ในกรณีนี้เราเห็นว่าเราสามารถใช้ประโยชน์ได้เนื่องจากเราไม่ได้ตรวจสอบว่าตัวแปรที่เราใช้เป็นตัวเลขนั้นเป็นตัวเลขจริงๆ ใน PHP คุณควรใช้ชุดฟังก์ชันอย่างกว้างขวางเพื่อตรวจสอบว่าอินพุตเป็นจำนวนเต็มลอยตัวเลขและอื่น ๆ แต่เมื่อพูดถึง SQL คุณควรคำนึงถึงคุณค่าส่วนใหญ่ของคำสั่งที่เตรียมไว้ รหัสข้างต้นจะปลอดภัยหากเป็นคำสั่งที่เตรียมไว้เนื่องจากฟังก์ชันฐานข้อมูลจะทราบว่า1 OR 1=1
ไม่ใช่ตัวอักษรที่ถูกต้อง
สำหรับhtmlspecialchars()
. นั่นเป็นที่วางทุ่นระเบิดของตัวเอง
มีปัญหาที่แท้จริงใน PHP เนื่องจากมีฟังก์ชันการหลบหนีที่เกี่ยวข้องกับ html ที่แตกต่างกันและไม่มีคำแนะนำที่ชัดเจนว่าฟังก์ชันใดทำหน้าที่อะไร
ประการแรกหากคุณอยู่ในแท็ก HTML แสดงว่าคุณประสบปัญหาอย่างแท้จริง ดูที่
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
เราอยู่ในแท็ก HTML แล้วดังนั้นเราจึงไม่จำเป็นต้อง <หรือ> ทำอะไรที่เป็นอันตราย เวกเตอร์การโจมตีของเราอาจเป็นได้javascript:alert(document.cookie)
ตอนนี้ HTML ผลลัพธ์ดูเหมือน
<img src= "javascript:alert(document.cookie)" />
การโจมตีผ่านเข้าไปโดยตรง
มันแย่ลง ทำไม? เพราะhtmlspecialchars
(เมื่อเรียกแบบนี้) เข้ารหัสเฉพาะอัญประกาศคู่และไม่เข้ารหัสเดี่ยว ดังนั้นถ้าเรามี
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
ผู้โจมตีที่ชั่วร้ายของเราสามารถฉีดพารามิเตอร์ใหม่ทั้งหมดได้แล้ว
pic.png' onclick='location.href=xxx' onmouseover='...
ให้เรา
<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
ในกรณีเหล่านี้ไม่มีสัญลักษณ์แสดงหัวข้อย่อยเวทย์มนตร์คุณเพียงแค่ต้องปรับแต่งอินพุตด้วยตัวคุณเอง หากคุณพยายามกรองตัวละครที่ไม่ดีออกไปคุณจะต้องล้มเหลวอย่างแน่นอน ใช้วิธีการอนุญาตพิเศษและปล่อยผ่านตัวอักษรที่ดีเท่านั้น ดูตารางสรุปXSSสำหรับตัวอย่างว่าเวกเตอร์มีความหลากหลายได้อย่างไร
แม้ว่าคุณจะใช้htmlspecialchars($string)
นอกแท็ก HTML คุณก็ยังเสี่ยงต่อเวกเตอร์การโจมตีแบบชาร์ตแบบหลายไบต์
ประสิทธิภาพสูงสุดที่คุณสามารถทำได้คือการใช้การรวมกันของ mb_convert_encoding และ htmlentities ดังนี้
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');
แม้สิ่งนี้จะทำให้ IE6 มีช่องโหว่เนื่องจากวิธีจัดการกับ UTF อย่างไรก็ตามคุณสามารถเปลี่ยนกลับไปใช้การเข้ารหัสที่ จำกัด มากขึ้นเช่น ISO-8859-1 ได้จนกว่าการใช้งาน IE6 จะลดลง
สำหรับการศึกษาเชิงลึกเพิ่มเติมเกี่ยวกับปัญหาหลายไบต์โปรดดูที่https://stackoverflow.com/a/12118602/1820
$result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'";
2. ในกรณีที่สอง (แอตทริบิวต์ที่มี URL) ไม่มีประโยชน์ใด ๆhtmlspecialchars
เลย ในกรณีเหล่านี้คุณควรเข้ารหัสอินพุตโดยใช้รูปแบบการเข้ารหัส URL เช่นการใช้rawurlencode
. ด้วยวิธีนี้ผู้ใช้ไม่สามารถแทรกjavascript:
และคณะได้
Take a whitelist approach and only let through the chars which are good.
บัญชีดำมักจะพลาดบางสิ่งบางอย่างไป +1
นอกจากคำตอบที่ยอดเยี่ยมของ Cheekysoft แล้ว:
ไม่มีสัญลักษณ์แสดงหัวข้อย่อยสีเงินสำหรับการป้องกันการแทรก HTML (เช่นการเขียนสคริปต์ข้ามไซต์) แต่คุณอาจทำได้ง่ายขึ้นหากคุณใช้ไลบรารีหรือระบบเทมเพลตเพื่อส่งออก HTML อ่านเอกสารสำหรับวิธีการหลบหนีอย่างเหมาะสม
ใน HTML สิ่งที่ต้องหลีกเลี่ยงแตกต่างกันขึ้นอยู่กับบริบท โดยเฉพาะอย่างยิ่งกับสตริงที่วางไว้ใน Javascript
ฉันเห็นด้วยกับโพสต์ข้างต้นอย่างแน่นอน แต่ฉันมีสิ่งเล็กน้อยที่จะตอบกลับคำตอบของ Cheekysoft โดยเฉพาะ:
เมื่อพูดถึงการสืบค้นฐานข้อมูลให้พยายามใช้แบบสอบถามที่กำหนดพารามิเตอร์ไว้เสมอ ไลบรารี mysqli และ PDO รองรับสิ่งนี้ สิ่งนี้ปลอดภัยกว่าการใช้ฟังก์ชัน Escape เช่น mysql_real_escape_string อย่างไม่มีที่สิ้นสุด
ใช่ mysql_real_escape_string เป็นเพียงฟังก์ชันหนีสตริงเท่านั้น มันไม่ใช่กระสุนวิเศษ สิ่งที่ต้องทำคือหลีกเลี่ยงอักขระอันตรายเพื่อให้สามารถใช้ในสตริงการสืบค้นเดียวได้อย่างปลอดภัย อย่างไรก็ตามหากคุณไม่ทำความสะอาดอินพุตของคุณล่วงหน้าคุณจะเสี่ยงต่อเวกเตอร์การโจมตีบางอย่าง
ลองนึกภาพ SQL ต่อไปนี้:
$ result = "เลือกช่องจากตาราง WHERE id =" .mysql_real_escape_string ($ _ POST ['id']);
คุณควรจะเห็นว่าสิ่งนี้เสี่ยงต่อการถูกใช้ประโยชน์ ลองนึกภาพพารามิเตอร์ id มีเวกเตอร์การโจมตีทั่วไป:
1 หรือ 1 = 1
ไม่มีตัวอักษรที่มีความเสี่ยงในการเข้ารหัสดังนั้นมันจะผ่านตัวกรองการหลบหนีโดยตรง ออกจากเรา:
เลือกช่องจากตาราง WHERE id = 1 หรือ 1 = 1
ฉันเขียนโค้ดฟังก์ชั่นเล็ก ๆ น้อย ๆ ที่ฉันใส่ไว้ในคลาสฐานข้อมูลของฉันซึ่งจะตัดสิ่งที่ไม่ใช่ตัวเลขออกไป มันใช้ preg_replace ดังนั้นจึงมีฟังก์ชั่นที่ได้รับการปรับให้เหมาะสมมากขึ้นเล็กน้อย แต่มันใช้งานได้ในพริบตา ...
function Numbers($input) {
$input = preg_replace("/[^0-9]/","", $input);
if($input == '') $input = 0;
return $input;
}
ดังนั้นแทนที่จะใช้
$ result = "เลือกช่องจากตาราง WHERE id =" .mysqlrealescapestring ("1 หรือ 1 = 1");
ฉันจะใช้
$ result = "เลือกช่องจากตาราง WHERE id =" .Numbers ("1 หรือ 1 = 1");
และจะเรียกใช้แบบสอบถามได้อย่างปลอดภัย
เลือกช่องจากตาราง WHERE id = 111
แน่นอนว่ามันหยุดไม่ให้แสดงแถวที่ถูกต้อง แต่ฉันไม่คิดว่านั่นเป็นปัญหาใหญ่สำหรับใครก็ตามที่พยายามฉีด sql ลงในไซต์ของคุณ)
return preg_match('/^[0-9]+$/',$input) ? $input : 0;
ส่วนสำคัญของปริศนานี้คือบริบท มีคนส่ง "1 หรือ 1 = 1" เนื่องจาก ID ไม่ใช่ปัญหาหากคุณอ้างทุกอาร์กิวเมนต์ในคำถามของคุณ:
SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"
ซึ่งส่งผลให้:
SELECT fields FROM table WHERE id='1 OR 1=1'
ซึ่งไม่ได้ผล เนื่องจากคุณกำลังหลีกเลี่ยงสตริงอินพุตจึงไม่สามารถแยกออกจากบริบทสตริงได้ ฉันได้ทดสอบสิ่งนี้จนถึงเวอร์ชัน 5.0.45 ของ MySQL และการใช้บริบทสตริงสำหรับคอลัมน์จำนวนเต็มไม่ก่อให้เกิดปัญหาใด ๆ
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
ทำงานได้ดีและดียิ่งขึ้นบนระบบ 64 บิต ระวังข้อ จำกัด ของระบบของคุณในการกำหนดหมายเลขจำนวนมาก แต่สำหรับรหัสฐานข้อมูลจะใช้งานได้ดีถึง 99% ของเวลา
คุณควรใช้ฟังก์ชัน / วิธีการเดียวในการทำความสะอาดค่าของคุณด้วย แม้ว่าฟังก์ชันนี้จะเป็นเพียง Wrapper สำหรับ mysql_real_escape_string () ทำไม? เนื่องจากวันหนึ่งเมื่อพบการใช้ประโยชน์จากวิธีการทำความสะอาดข้อมูลที่คุณต้องการคุณจะต้องอัปเดตที่เดียวแทนที่จะค้นหาและแทนที่ทั้งระบบ
ทำไมโอ้ทำไมคุณไม่ใส่เครื่องหมายคำพูดรอบอินพุตของผู้ใช้ในคำสั่ง sql ของคุณ? ดูเหมือนจะโง่มากที่จะไม่! รวมถึงเครื่องหมายคำพูดในคำสั่ง sql ของคุณจะทำให้ "1 หรือ 1 = 1" เป็นความพยายามที่ไร้ผลใช่หรือไม่?
ตอนนี้คุณจะพูดว่า "จะเกิดอะไรขึ้นถ้าผู้ใช้ใส่เครื่องหมายคำพูด (หรือเครื่องหมายคำพูดคู่) ในอินพุต"
ดีและแก้ไขได้ง่ายเพียงแค่ลบคำพูดที่ป้อนโดยผู้ใช้ เช่น: input =~ s/'//g;
. ตอนนี้ดูเหมือนว่าสำหรับฉันแล้วการป้อนข้อมูลของผู้ใช้นั้นจะปลอดภัย ...