SQL Injection (2)
บทความนี้ไม่ได้มุ่งหวังเพื่อให้เกิดผลเสียแก่ใครหรือกระทั่งเว็บตัวอย่าง ผมไม่ได้กระทำการทดสอบกับเว็บตัวอย่างจริงๆ เพียงแต่บอกเล่าตามที่มีคนบอกมาอีกที
จุดประสงค์ของบทความนี้เพื่อเป็นตัวอย่าง และใช้ความระมัดระวังกับเว็บไซต์ของตัวเองเท่านั้น
พอดีวันนี้มีรุ่นน้องคนนึง มาถามผมว่ารู้จัก เจ้าของเว็บ เว็บนึงมั้ย เขาบอกว่าเขาสามารถ hack เว็บนึงได้ เขาแจ้งไปแล้วแต่ทางเว็บเจ้าของเพิกเฉย เขาก็เลยเอามาเล่าให้ผมฟัง
เขาบอกผมว่าที่เขาพบ มันเป็น SQL Injection ซึ่งก็หมายถึงการ Hack เว็บผ่าน MySQL และก็ค่อนข้างอันตรายด้วย เพราะสิ่งที่ได้มาเป็นชื่อ หรือ รหัสผ่านของแอดมิน ถ้าเป็นเว็บที่สำคัญก็ค่อนข้างอันตรายมั้ยครับ
มาดูโค้ดกันก่อนเลย
index.php?name=xxx&category=1+and+1=2+union+select+concat(username,0x3A,password)+from+tablename/*
เป็นการ hack โดยผ่าน query ธรรมดาครับ ปัญหาของกรณีนี้เกิดจากการไม่ได้ตรวจสอบ query ที่ส่งเข้ามาก่อน ในที่นี้คือส่งผ่านตัวแปร category เมือรับเอาไปแล้วก็ไปประมวลผล SQL เลย ทำให้ได้ผลลัพท์ที่ไม่พึงประสงค์ออกมา
โค้ดในส่วนที่รับค่ามาประมวลผลมี ดังนี้
"SELECT xxx FROM ".xxx." WHERE id=".$_GET[category]." ";
จะเห็นได้ว่าเมื่อรับ query เข้ามาแล้วไม่ได้มีการตรวจสอบใดๆ แล้วยังเอามาใส่ลงใน query เลย ผลลัพท์เมื่อเอาใส่ลง query แล้วเป็นดังนี้
SELECT xxx FROM xxx WHERE id=1 and 1=2 union select concat(username,0x3A,password) from web_admin/*
นอกจากนี้ ในตอนแสดงผลยังเอาค่าที่ query ได้ออกมาแสดงตรงๆ อีก
$array = mysql_fetch_array( $query );
echo $array[0];
// หรือการใช้
$result = mysql_result( $query , 0 , 0 );
echo $result;
ซึ่งทั้ง 2 วิธีเป็นการแสดงผลลัพท์โดยไม่ได้ระบุชื่อฟิลด์ที่ต้องการ ซึ่งหากมีผลลัพท์ออกมา มันก็สามารถแสดงผลได้ทันที ซึ่งอาจไม่ใช่ผลลัพท์ที่ต้องการก็ได้
ปัญหาที่เกิดผมเข้าใจว่าผู้ออกแบบคงคาดว่าค่าตัวแปร category จะเป้นตัวเลขเพียงอย่างเดียว ก็เลยไม่ได้ทดสอบอะไร แต่ในความเป็นจริงค่อนข้างอันตราย เพราะเป็นการเปิดช่องให้ผู้ไม่หวังดีใช้ช่องทางนี้ได้
สำหรับการป้องกัน ก็ไม่มีอะไรมาก
1. ทำการทดสอบตัวแปรด้วย (int)
$category = (int)$_GET[category];
เพื่อรับค่าที่เป็นตัวเลขเท่านั้น
2.ใส่ ' (quote) ครอบตัวแปร เพื่อกำหนดขอบเขตของตัวแปร หากเป็นสตริงค์ หรือหากเป็นตัวเลขก็ได้
"SELECT xxx FROM ".xxx." WHERE id='".$_GET[category]."' ";
ส่วนในการแสดงผล ควรระบุชื่อฟิลด์เพื่อรับค่า
$array = mysql_fetch_array( $query );
echo $array[category];
// หรือการใช้
$result = mysql_result( $query , 0 , 'category' );
echo $result;
ขอให้โชคดีมีชัยนะครับ