หลังจากได้เข้าร่วมอบรม Website Security Monitoring ที่ทางสำนักวิทยบริการและเทคโนโลยีสารสนเทศ ของมหาวิทยาลัยจัดขึ้น ก็ได้ความรู้เรื่องการสร้างระบบล็อกอินเพื่อป้องกันปัญหา SQL Injection มาฝากกับผู้เริ่มต้นทุกท่าน
สำหรับการป้องกันนั้นจะมีอยู่หลายวิธี และวิธีหลักที่ใช้กันก็คือ
กรองด้วยฟังก์ชั่น
แต่ในตัวอย่างของบทความนี้ จะอธิบายทีละส่วนเพื่อให้เห็นถึงวิธีการต่างๆ ที่จะโยงไปยังเรื่องของการใช้ md5 ในบทความต่อๆไปด้วยครับ
จากบทความที่เคยนำเสนอเกี่ยวกับการสร้างแบบฟอร์มล็อกอิน
จะใช้ SQL ในการตรวจสอบข้อมูลแบบเบื้องต้นดังนี้
ตัวอย่างฐานข้อมูลตารางผู้ใช้งาน
เมื่อล็อกอินด้วยชื่อ sunzandesign และรหัสผ่าน abcd จะสามารถเข้าใช้งานได้
ก็จะได้ผลลัพธ์การล็อกอิน ดังนี้
แต่ถ้าเรากรอกชื่อผู้ใช้งานผิด การล็อกอินก็จะแสดงข้อผิดพลาด และให้ล็อกอินใหม่
สังเกตว่าเรากรอกรหัสผ่านว่า admin ก็จะถูกนำไปสร้าง SQL สำหรับคิวรี่เรียกข้อมูลจากฐานข้อมูลตรงๆ ดังนั้นการที่เราจะแฮกเข้าไปในยูสเซอร์ที่เราต้องการ ก็เพียงแค่ใช้ชื่อยูสเซอร์ sunzandesign และรหัสอะไรก็ได้ ตามด้วย OR เพื่อให้แทรกลงไปใน SQL ให้เกิดการทำงานในแบบที่เราต้องการ
ตัวอย่างเช่น
ชื่อผู้ใช้ = sunzandesign
รหัสผ่าน = xxxx' OR 'A'='A
ผลลัพธ์ที่ได้คือ ล็อกอินผ่าน!!! เพราะอะไร???
เพราะเราเพิ่มเงื่อนไขเข้าไปคือ นอกจากชื่อรหัสผ่านต้องตรงกับฐานข้อมูลแล้ว
OR 'A' = 'A' ถ้า A มีค่าเท่ากับ A ก็ให้ผ่านไปได้ ((ซึ่งมันก็ต้องตรงกันอยู่วันยังค่ำ = ='))
ถ้าลองนำโค๊ดที่ได้ไปลองใน phpMyAdmin จะได้ผลลัพธ์ดังนี้
ตอนนี้ก็ทราบกันแล้วว่า SQL แบบที่นำเสนอไปนั้นไม่สามารถใช้กับระบบล็อกอินได้เลย
((รวมถึงหน้าอื่นๆทุกหน้าที่เขียน SQL แบบธรรมดาที่ไม่มีการป้องกันแบบตัวอย่างนี้ ล้วนเป็นช่องโหว่ทั้งหมด >O< ))
แล้วเราจะมีวิธีป้องกัน SQL injection อย่างไร???
ก็ใช้ฟังก์ชั่นดังที่ได้แนะนำไว้ก่อนหน้านี้นั่นก็คือ
ในกรณีรับข้อมูลที่เป็น string (หรือจะเป็น integer ก็ได้)
mysql_real_escape_string()
mysqli_real_escape_string()
หรือจะใช้วิธี md5() รหัสผ่านไปเลยก็ได้(ซึ่งจริงๆแล้วรหัสผ่านก็ควรจะเข้ารหัสไว้แล้ว) โค๊ดที่ใช้ก็จะเปลี่ยนไปเป็นดังนี้
ก่อนอื่นก็เข้ารหัสให้กับรหัสผ่านเดิมทั้งหมดในฐานข้อมูลก่อน ก็จะได้ข้อมูลดังภาพ
ทดลองกรอกข้อมูลที่ถูกต้องในการล็อกอินเพื่อทดสอบ
จะได้ผลลัพธ์ที่ถูกต้อง
abcd => md5() => e2fc714c4727ee9395f324cd2e7f331f
((ตรงกับข้อมูลในฐานข้อมูล))
แต่ถ้าลองกรอกข้อมูลเพื่อใช้เทคนิค SQL Injection ก็จะไม่สามารถล็อกอินได้
เพราะข้อมูลรหัสผ่านไม่ตรงกับในฐานข้อมูล
xxxx' OR 'A'='A => md5() => c47fef686224b37f5f3350dfae45ca15
((ไม่ตรงกับข้อมูลในฐานข้อมูล))
และวิธีสุดท้ายที่จะนำเสนอในบทความนี้ หากท่านไม่สามารถใช้งาน mysql_real_escape_string() และไม่พร้อมที่ md5() รหัสผ่าน (หรือเข้ารหัสผ่านแบบอื่นๆ) ให้ลองใช้วิธีแยกรหัสผ่านมาตรวจสอบต่างหากด้วยฟังก์ชั่น if else ของ PHP
สำหรับการป้องกันนั้นจะมีอยู่หลายวิธี และวิธีหลักที่ใช้กันก็คือ
กรองด้วยฟังก์ชั่น
mysql_real_escape_string() , mysqli_real_escape_string() ,
(ดูเพิ่มเติมได้ที่ http://php.net/manual/en/function.mysql-real-escape-string.php )
ในกรณีของ id ที่เป็นตัวเลขล้วน ก็สามารถใช้ (int) วางไว้ข้างหน้าตัวแปรเพื่อป้องกันข้อมูลในหน้าอื่นๆได้เช่นกัน
แต่ในตัวอย่างของบทความนี้ จะอธิบายทีละส่วนเพื่อให้เห็นถึงวิธีการต่างๆ ที่จะโยงไปยังเรื่องของการใช้ md5 ในบทความต่อๆไปด้วยครับ
จากบทความที่เคยนำเสนอเกี่ยวกับการสร้างแบบฟอร์มล็อกอิน
จะใช้ SQL ในการตรวจสอบข้อมูลแบบเบื้องต้นดังนี้
$sql = "SELECT * FROM tb_user WHERE user_name = '$_POST[username]'
AND user_password = '". $_POST['password'] ."' ";
ตัวอย่างฐานข้อมูลตารางผู้ใช้งาน
เมื่อล็อกอินด้วยชื่อ sunzandesign และรหัสผ่าน abcd จะสามารถเข้าใช้งานได้
ก็จะได้ผลลัพธ์การล็อกอิน ดังนี้
แต่ถ้าเรากรอกชื่อผู้ใช้งานผิด การล็อกอินก็จะแสดงข้อผิดพลาด และให้ล็อกอินใหม่
สังเกตว่าเรากรอกรหัสผ่านว่า admin ก็จะถูกนำไปสร้าง SQL สำหรับคิวรี่เรียกข้อมูลจากฐานข้อมูลตรงๆ ดังนั้นการที่เราจะแฮกเข้าไปในยูสเซอร์ที่เราต้องการ ก็เพียงแค่ใช้ชื่อยูสเซอร์ sunzandesign และรหัสอะไรก็ได้ ตามด้วย OR เพื่อให้แทรกลงไปใน SQL ให้เกิดการทำงานในแบบที่เราต้องการ
ตัวอย่างเช่น
ชื่อผู้ใช้ = sunzandesign
รหัสผ่าน = xxxx' OR 'A'='A
ผลลัพธ์ที่ได้คือ ล็อกอินผ่าน!!! เพราะอะไร???
เพราะเราเพิ่มเงื่อนไขเข้าไปคือ นอกจากชื่อรหัสผ่านต้องตรงกับฐานข้อมูลแล้ว
OR 'A' = 'A' ถ้า A มีค่าเท่ากับ A ก็ให้ผ่านไปได้ ((ซึ่งมันก็ต้องตรงกันอยู่วันยังค่ำ = ='))
ถ้าลองนำโค๊ดที่ได้ไปลองใน phpMyAdmin จะได้ผลลัพธ์ดังนี้
ตอนนี้ก็ทราบกันแล้วว่า SQL แบบที่นำเสนอไปนั้นไม่สามารถใช้กับระบบล็อกอินได้เลย
((รวมถึงหน้าอื่นๆทุกหน้าที่เขียน SQL แบบธรรมดาที่ไม่มีการป้องกันแบบตัวอย่างนี้ ล้วนเป็นช่องโหว่ทั้งหมด >O< ))
แล้วเราจะมีวิธีป้องกัน SQL injection อย่างไร???
ก็ใช้ฟังก์ชั่นดังที่ได้แนะนำไว้ก่อนหน้านี้นั่นก็คือ
ในกรณีรับข้อมูลที่เป็น string (หรือจะเป็น integer ก็ได้)
mysql_real_escape_string()
mysqli_real_escape_string()
$sql = "SELECT * FROM tb_user
WHERE user_name = '". mysql_real_escape_string($_POST['username']) ."'
AND user_password = '". mysql_real_escape_string($_POST['password']) ."' ";
หรือจะใช้วิธี md5() รหัสผ่านไปเลยก็ได้(ซึ่งจริงๆแล้วรหัสผ่านก็ควรจะเข้ารหัสไว้แล้ว) โค๊ดที่ใช้ก็จะเปลี่ยนไปเป็นดังนี้
$sql = "SELECT * FROM tb_user
WHERE user_name = '". $_POST['username'] ."'
AND user_password = '". md5($_POST['password']) ."' ";
ก่อนอื่นก็เข้ารหัสให้กับรหัสผ่านเดิมทั้งหมดในฐานข้อมูลก่อน ก็จะได้ข้อมูลดังภาพ
ทดลองกรอกข้อมูลที่ถูกต้องในการล็อกอินเพื่อทดสอบ
จะได้ผลลัพธ์ที่ถูกต้อง
abcd => md5() => e2fc714c4727ee9395f324cd2e7f331f
((ตรงกับข้อมูลในฐานข้อมูล))
แต่ถ้าลองกรอกข้อมูลเพื่อใช้เทคนิค SQL Injection ก็จะไม่สามารถล็อกอินได้
เพราะข้อมูลรหัสผ่านไม่ตรงกับในฐานข้อมูล
xxxx' OR 'A'='A => md5() => c47fef686224b37f5f3350dfae45ca15
((ไม่ตรงกับข้อมูลในฐานข้อมูล))
และวิธีสุดท้ายที่จะนำเสนอในบทความนี้ หากท่านไม่สามารถใช้งาน mysql_real_escape_string() และไม่พร้อมที่ md5() รหัสผ่าน (หรือเข้ารหัสผ่านแบบอื่นๆ) ให้ลองใช้วิธีแยกรหัสผ่านมาตรวจสอบต่างหากด้วยฟังก์ชั่น if else ของ PHP
$sql = "SELECT user_password FROM tb_user
WHERE user_name = '". $_POST['username'] ."'";
$qry = mysql_query($sql) or die(mysql_error());
$row = mysql_fetch_assoc($qry);
if(!empty($row) && $row['user_password'] == $_POST['password']){
echo "<h3 style='color:green'>Login OK!</h3>";
}else{
echo "<h3 style='color:red'>Login Fail!</h3>";
}
"PHP ไม่ได้สร้างสุดยอดโปรแกรม แต่ PHP ช่วยให้งานคุณง่ายขึ้นต่างหาก"
PHP CI MANIA - PHP Code Generator
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม
สนับสนุนค่ากาแฟผู้เขียนได้ที่
ความคิดเห็น
แสดงความคิดเห็น