สวัสดีครับในบทความนี้จะเป็นการแนะนำการป้องกันเว็บไซต์ของเราจากการถูกโจมตีด้วย MySQL Injection ซึ่ง MySQL Injection คือการที่ผู้ไม่ประสงค์ดีสามารถแทรกโค้ดเข้าไปใน คำสั่ง MySQL ของเราได้ ซึ่งมีตัวอย่างดังนี้
สมมุตว่าผมต้องการทำระบบ ล๊อคอิน ผมก็จะเขียน PHP ประมาณนี้
//login.php
$username = $_POST['username'];
$password = $_POST['password'];
$check_login = mysqli_query($db, "SELECT * FROM admin WHERE username = '".$username."' AND password = '".$password."'");
if(mysqli_num_rows($check_login) == 1) {
echo "Login Successfully.";
} else {
echo "Login Failed."
}
เมื่อกรอกข้อมูลเข้าไปจะได้ query ที่หน้าตาแบบนี้
SELECT * FROM admin WHERE username = 'admin' AND password = '123';
หาก username และ password ตรงกับ ข้อมูลในฐานข้อมูล ก็จะถือว่่าข้อมูลการเข้าสู่ระบบนั้นถูกต้อง แต่หากใส่ข้อมูลในส่วนของ password ว่า 'OR 1=1
สิ่งที่จะเกิดขึ้นคือ แม้ข้อมูลจะไม่ตรงกับในฐานข้อมูล แต่ผู้ไม่หวังดีจะสามารถเข้าสู่ระบบได้อย่างง่ายดาย เนื่องจาก
เครื่องหมาย ' จะไปปิดเครื่องหมาย ' ที่อยู่ก่อนหน้า ทำให้ข้อความหลังจาก ' จะไม่ได้อยู่ใน String อีกต่อไป จะถือว่าเป็นคำสั่งของ MySQL พอมาเจอ OR 1=1 มันคือตรรกะที่เป็นจริงเสมอ ดังนั้น หากแปลคำสั่ง SQL มาเป็นข้อความตรรกะ จะได้ว่า
SELECT * FROM admin WHERE username = 'admin' AND password = '' OR 1=1;
เลือก ทั้งหมด จาก admin เมื่อ username = admin และ password = '' หรือ 1=1
จะพบว่าตรรกะนี้จะเป็นจริงเสมอ ทำให้สามารถเรียกแถวดังกล่าวได้ เพียงแค่ใช้ username ซึ่งวิธีการทำ SQL Injection นั้นมีมากมาย หลายวิธี ไม่ไ้ด้มีแค่ตรรกะ OR 1=1 เพียงอย่างเดียว และสามารถโจมตีคำสั่ง SELECT ในแบบอื่นได้ ผู้ไม่หวังดีสามารถใช้ช่องโหว่นี้ในการแทรกคำสั่ง SQL อื่นๆ เข้าไป เพื่อแสดงข้อมูลในตารางอื่นๆ ออกมาได้
วิธีแรก คือการใช้ฟังก์ชั่น mysqli_real_escape_string() ฟังก์ชั่นนี้ทำงานโดยการตัดคำสั่งของ MySQL ออกจาก Strings โดยในกรณีนี้จะเอามาครอบ $_POST ไว้
//login.php
$username = mysqli_real_escape_string($db, $_POST['username']);
$password = mysqli_real_escape_string($db, $_POST['password']);
$check_login = mysqli_query($db, "SELECT * FROM admin WHERE username = '".$username."' AND password = '".$password."'");
if(mysqli_num_rows($check_login) == 1) {
echo "Login Successfully.";
} else {
echo "Login Failed."
}
วิธีที่สอง ซึ่งได้รับความนิยมมากที่สุด คือการ bind_params คือการที่เราจะใช้ฟังก์ชั่น mysqli_prepare() ก่อน และจะ bind params ซึ่งกรณีตัวอย่างคือ $username $password เข้าไปในคำสั่ง SQL ทีหลัง ตัวอย่างดังนี้
//login.php
$username = $_POST['username'];
$password = $_POST['password'];
$check_login = mysqli_prepare($db, "SELECT * FROM admin WHERE username = ? AND password = ?");
mysqli_stmt_bind_param($check_login, 'ss', $username, $password);
mysqli_stmt_execute($check_login);
mysqli_stmt_store_result($check_login);
if(mysqli_stmt_num_rows($check_login) == 1) {
echo "Login Successfully.";
} else {
echo "Login Failed."
}
อธิบายเพิ่มเติมในฟังก์ชั่น mysqli_stmt_bind_param() ในส่วน 'ss' คือการแทนที่ $username กับ $password ลงไปใน ? ตามลำดับ โดยที่ตัวอักษร s หมายถึงชนิดของข้อมูลที่เป็น String
อ้างอิง:
www.w3schools.com/php/func_mysqli_real_escape_string.asp
www.tutorialspoint.com/php/php_function_mysqli_stmt_bind_param.htm
