SQL Injection

ในปัจจุบันที่ระบบฐานข้อมูลเป็นหัวใจสำคัญในการเก็บข้อมูลต่าง ๆ ของเว็บไซต์ ช่องโหว่หนึ่งที่แฮกเกอร์มักใช้ในการโจมตีเว็บไซต์เหล่านี้ก็คือการทำ SQL Injection การโจมตีด้วยวิธีดังกล่าวแฮกเกอร์สามารถตรวจสอบว่าเว็บใด ๆ มีช่องโหว่ที่สามารถทำ SQL Injection ได้หรือไม่ได้อย่างง่ายดายและการโจมตีด้วย SQL Injection ก็ไม่ใช่เป็นเรื่องที่ยากเย็นอะไร แต่ผลลัพท์ที่ได้นั่นรุนแรงทีดียว ปัญหาก็คือโปรแกรมเมอร์มือใหม่ส่วนใหญ่ไม่รู้ด้วยซ้ำว่า SQL Injection คืออะไร?

การโจมตีแบบ SQL Injection คือการที่ hacker ใช้ช่องโหว่ของโปรแกรมทำให้สามารถเติมคำสั่งภาษา SQL เข้าไปทางส่วนรับข้อมูลขาเข้า(input)ของโปรแกรมได้. หากเว็บแอพพลิเคชั่นที่เปิดช่องให้แฮกเกอร์ทำการโจมตีแบบนี้ได้ ก็จะทำให้แฮกเกอร์สามารถอ่านข้อมูลสำคัญต่าง ๆ ในระบบฐานข้อมูล (SELECT), แก้ไบข้อมูลต่าง ๆ ในระบบฐานข้อมูล( INSERT/UPDATE/DELETE), เรียกคำสั่งที่ใช้ในการจัดการระบบฐานข้อมูลเช่นคำสั่งสำรองข้อมูล (backup) หรือแม้กระทั่งเรียกคำสั่งของระบบปฎิบัติการได้ในบางกรณื.

SQL Injection จะเกิดขึ้นได้ต้องมีปัจจัยสองประการ

  1. โปรแกรมรับข้อมูลจากแหล่งที่ไม่น่าเชื่อถือต่าง ๆ เช่น Form Input, Query String, Cookie เป็นต้น ซึ่งข้อมูลดังกล่าวไม่มีการตรวจสอบความถูกต้องของข้อมูลก่อน ยกตัวอย่างเช่น หากข้อมูลที่รับเข้ามานั้นคือหมายเลขโทรศัพท์ ก็ไม่ได้ตรวจสอบว่าข้อมูลดังกล่าวจะต้องประกอบด้วยตัวเลขเท่านั้น หรือ ไม่ได้ตรวจสอบว่าข้อมูลดังกล่าวมีตัวอักษรอันตรายที่ใช้ในภาษา SQL เช่น single quote ('), dash (-), back slash (\) หรือไม่ เป็นต้น
  2. ข้อมูลที่ไม่ได้ผ่านการตรวจสอบดังกล่าวถูกนำใช้ในการสร้างคำสั่ง SQL แบบไดนามิก

แฮกเกอร์จึงสามารถใช้ข้อบกพร่องข้างต้นสร้างข้อมูลที่จะป้อนให้กับโปรแกรมแบบพิเศษเพื่อทำให้เรียกใช้คำสั่ง SQL ต่างๆ เพิ่มเติมจากที่โปรแกรมปกติเรียกใช้ได้ ตัวอย่างด้านล่างคือ ส่วนของโปรแกรมภาษาจาวาที่้ใช้ในการตรวจสอบผู้ใช้ (Authenticate) ทั่วๆไป แต่โปรแกรมดังกล่าวมีข้อบกพร่องที่แฮกเกอร์สามารถใช้เป็นช่องทางในการโจมตีแบบ SQL Injection ได้

String DRIVER = "com.ora.jdbc.Driver"; String DataURL = "jdbc:db://localhost:5112/users"; String LOGIN = "admin"; String PASSWORD = "admin123"; Class.forName(DRIVER); //Make connection to DB Connection connection = DriverManager.getConnection(DataURL, LOGIN, PASSWORD); String Username = request.getParameter("USER"); // From HTTP request String Password = request.getParameter("PASSWORD"); // From HTTP request int iUserID = -1; String sLoggedUser = ""; String queryString = "SELECT User_id, Username FROM USERS " + " WHERE Username = '" +Username + "' AND Password = '" + Password + "'"; Statement selectStatement = connection.createStatement (); ResultSet resultSet = selectStatement.executeQuery(queryString); if (resultSet.next()) { iUserID = resultSet.getInt(1); sLoggedUser = resultSet.getString(2); } PrintWriter writer = response.getWriter (); if (iUserID >= 0) { writer.println ("User logged in: " + sLoggedUser); } else { writer.println ("Access Denied!") }

เมื่อโปรแกรมดังกล่าวถูกเรียกใช้ จะเห็นว่ามีการรับข้อมูลมาจาก HTTP Request มาสองอันคือ Username กับ Password หลังจากนั้นข้อมูลทั้งสองถูกนำไปใช้ในการสร้างคำสั่งภาษา SQL โดยตรงโดยไม่ได้มีการตรวจสอบก่อนว่าข้อมูลที่รับเข้ามาทาง HTTP Request ทั้งสองนั้นปลอดภัยหรือไม่ (ไม่ได้ตรวจสอบว่ามีตัวอักษรที่เป็นอันตรายในการสร้างคำสั่ง SQL หรือไม่ นอกจากนี้ยังไม่ได้ตรวจสอบความยาวของข้อมูลด้วยว่า ความยาวของข้อมูลที่รับมานั้นสั้นหรือยาวเกินไปหรือไม่ ข้อมูลที่ยาวเกินไปอาจจะทำให้เกิดปัญหาอื่น ๆ เช่น Buffer Overflow เป็นต้น)
ดังนั้นถ้าหากข้อมูลที่รับมาเป็นข้อมูลที่ปกติเช่น Username มีค่าเท่ากับ "John" และ Password มีค่าเท่ากับ "Smith123" คำสั่ง SQL ที่ถูกสร้างก็จะเป็นดังนี้

SELECT User_id, Username FROM USERS WHERE Username = 'John' AND Password = 'Smith123'

ซึ่งคำสั่งดังกล่าวจะไปดึงข้อมูลมาจากตารางที่ชื่อว่า USERS ที่มีค่าของฟิลด์ที่ชื่อว่า Username เท่ากับ 'John' และค่าของฟิลด์ที่ชื่อว่า Password = 'Smith123' ซึ่งถ้าในฐานข้อมูลมีข้อมูลดังกล่าวอย่างน้อย 1 เรคคอร์ด ผู้ใช้ก็จะสามารถเข้าสู่ระบบได้
แต่ถ้าหากแฮกเกอร์มาทดลองเจาะโปรแกรมนี้ เขาอาจจะส่ง Username มีค่าเท่ากับ "nobody" และ Password มีค่าเท่ากับ "nopassword' OR 'a'='a" คำสั่ง SQL ที่ถูกสร้างก็จะเป็นดังนี้

SELECT User_id, Username FROM USERS WHERE Username = 'nobody' AND Password = 'nopassword' OR 'a' = 'a'

ผลลัพธ์ที่คืนมากจากคำสั่ง SQL ด้านบนนั้นคือข้อมูลทุกเรคคอร์ดในตาราง USERS เนื่องจากในภาษา SQL นั้น OR operation มี precedence ต่ำกว่า AND operation และ 'a'='a' เป็นนิพจน์ที่เป็นจริงเสมอ ดังนั้นการ OR ด้วยนิพจน์ที่เป็นจริงเสมอจึงทำให้ระบบฐานข้อมูลคือข้อมูลทุกเรคคอร์ดในตาราง USERS และทำให้ผู้ใช้เ้ข้าสู่ระบบได้เหมือนผู้ใช้ทั่วไปโดยที่ไม่ต้องใส่ Username กับ Password ให้มีอยู่ในระบบฐานข้อมูลเลย แต่ก็มีคำถามที่เกิดขึ้นก็คือ ในกรณีนี้แฮกเกอร์เข้าไปในระบบโดยได้สิทธิของผู้ใช้คนไหน? คำตอบก็คือ เขาได้สิทธิเป็นผู้ใช้ที่มี Username ตรงกับข้อมูลเรคคอร์ดแรกที่ระบบฐานข้อมูลคืนมาให้ด้วยคำสั่ง SQL ดังกล่าว

การป้องกัน SQL Injection ที่ถูกวิธี

1. ใช้ Parameterized Queries แทน

ระบบฐานข้อมูลและ ภาษาคอมพิวเตอร์ส่วนใหญ่มักจัดเตรียม API ที่ในการติดต่อระบบฐานข้อมูลอย่างถูกวิธีเพื่อป้องกันการโจมตีด้วย SQL Injection ไว้ให้แล้ว ที่เรียกว่า Parameterized Queries หรือ Prepared Statements ซึ่งวิธีดังกล่าวจะมีขั้นตอนในการสร้างคำสั่งภาษา SQL ดังนี้

  1. โปรแกรมต้องประกาศโครงสร้างของคำสั่ง SQL และมีการกำหนดตำแหน่งซึ่งเมื่อมีการเรียกใช้คำสั่งนี้ ตำแหน่งดังกล่าวจะถูกแทนที่ด้วยข้อมูลต่าง ๆ ที่รับเข้ามา (parameters)
  2. โปรแกรมทำการแทนค่าข้อมูลต่าง ๆ ลงไปในตำแหน่งที่เตรียมไว้ในข้อที่ 1 ด้วย API ซึ่งจะจัดการกับข้อมูลเหล่านี้อย่างถูกวิธี ทำให้ไม่มีการใส่ condition หรือ คำสั่งต่าง ๆ เพิ่มหรือลดลงจากเดิมที่ประกาศไว้ในข้อที่ 1 ได้

ต่อไปนี้จะเป็นตัวอย่างการแก้ไขข้อบกพร่องของโปรแกรมด้านบนโดยเปลี่ยนมาใช้ Parameterized Queries แทน

/*กำหนดโครงสร้างของคำสั่ง SQL โดยเครื่องหมายคำถาม (?) จะแทนตำแหน่งที่จะถูกแทนที่ด้วยข้อมูลที่รับเข้ามา*/ String queryString = "SELECT User_id, Username FROM USERS " + " WHERE Username = ?" + " AND Password = ?"; /*สร้าง Parameterized Query*/ PreparedStatement pstmt = connection.prepareStatement(queryString ); /* แทนที่ค่าต่าง ๆ ลงในตำแหน่งที่เตรียมไว้*/ pstmt.setString(1, Username); pstmt.setString(2, Password); ResultSet resultSet = pstmt.executeQuery();

2. กำหนดสิทธิของโปรแกรมในการติดต่อกับระบบฐานข้อมูลให้มีสิทธิน้อยที่สุดเท่าที่จะเป็นไปได้

โดยปกติแล้วโปรแกรมต่าง ๆ ไม่จำเป็นที่จะต้อมีสิทธิในการเรียกใช้คำสั่ง SQL เท่ากับผู้ดูแลระบบฐานข้อมูล (DBA) ยกตัวอย่างเช่นหากเว็บไซต์ของคุณเป็นเพียงแค่เว็บไซต์ที่ใช้แสดงข้อมูลสินค้า และสั่งซื้อผ่านอินเตอร์เน็ต และจำเป็นต้องลงทะเบียนผู้ใช้ก่อนซื้อ ในการกำหนดสิทธืของเว็บไซต์นี้ ก็ควรกำหนดให้มีสิทธิอ่านอย่างเดียวสำหรับตารางข้อมูลสินค้า สิทธิอ่าน/เขียนสำหรับตารางข้อมูลการสั่งซื้อและตางรางข้อมูลลูกค้า และไม่มีสิทธิใด ๆ เลยใน table อื่น ๆ ที่ไม่เกี่ยวข้องเป็นต้น


3. ทำการ Hardening Database

ระบบฐานข้อมูลบางอันอาจจะมีความสามารถพิเศษต่าง ๆ เช่นสามารถเรียกใช้คำสั่งของระบบปฎิบัติการเป็นต้น ถ้าความสามารถพิเศษเหล่านี้ไม่จำเป็นต้องใช้ก็ต้องปิดมัน และหมั่นทำการ patch ระบบฐานข้อมูลเป็นประจำเพื่อป้องกันการโจมตีด้วย Known-exploits

ขอบคุณค่ะ
ได้ความรู้ขึ้นเยอะเลยคุ่ะ
:)

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd><img> <object> <embed> <param>
  • Lines and paragraphs break automatically.
  • Images can be added to this post.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.
ญาณรักข์ วรรณสาย