บทความในตอนนี้เป็นตอนต่อเนื่องที่พูดเกี่ยวกับจุดอ่อนที่เรามักพบกันได้บ่อย นั่นคือ SQL Injection โดยเป็นการโจมตีโดยการฉีค SQL query ผ่านช่องทางต่างๆ ที่ให้เราส่ง input จาก client ไปหาตัวระบบได้ เมื่อทำสำเร็จแล้วผู้โจมตีจะสามารถเข้าถึงข้อมูลเพื่อทำ CRUD ที่อยู่ในฐานข้อมูลได้ โดยไม่ต้องผ่านการตรวจสอบสิทธิของ application นั้นๆ
ผลกระทบของ SQL Injection ขึ้นอยู่กับปัจจัยเหล่านี้
- จุดที่ฉีคเข้ามา query
- สิทธิของ user ที่ execute query นั้น
- database platform และ version
- database configuration
String query = “Select * from Users Where username = ‘“ + request.getParameter(”username”) + “‘ and password = ‘“ + request.getParameter(”password”) + “‘“; try { Statement statement = connection.createStatement( … ); ResultSet results = statement.executeQuery( query ); }แล้วถ้าใช้ url ลักษณะนี้
http://www.yoursite.com/login?username=tofu’%20or%20‘1’%20=%20’1&password=1’%20or%20’1’%20=%20’1
เท่ากับเราจะได้ query ที่ค่าจะออกมาเป็น true และ query ผลลัพธ์ออกมาได้
Select * from Users Where username = ‘tofu’ or ‘1’=’1’ and password=’1’ or ‘1’=’1’นอกจากนี้ยังมี query อีกสารพัดแบบที่ถูกฉีคเข้ามาทดสอบความอ่อนแอของ application เราหาได้ใน google
แนวทางป้องกันหลัก
- Parameterized query
- ใช้ Prepared Statement / HQL / ...
- ใช้ Stored Procedures
- Character escaping
- User-input Handling ได้กล่าวไปในบท ก่อนหน้านี้ และดูเพิ่มเติมได้จาก นี้
- ซ่อน error message ที่มาจาก database server เนื่องจากการทำ SQL injection จะเกิด error message ที่ return ออกมาจาก
- database server ได้ซึ่ง message ดังกล่าวมันช่วยให้ผู้ไม่ประสงค์ดีนำไปวิเคราะห์ และนำกลับมาโจมตีเราใหม่
- ใช้ user ที่มีสิทธิ์เท่าที่จำเป็นต่อการ execute query นั้นเท่านั้น ไม่ควรใช้ sa, dba หรือ admin
String query = “Select * from Users Where username = ? and password = ?”; try { PrepareStatement pstmt = conn.prepareStatement(query); pstmt.setString(1, username); pstmt.setString(2, password); ResultSet results = pstmt.executeQuery(query); }ใน query language ของ framework ต่างๆ ก็มี parameter statment ให้ใช้ เช่น Hibernate Query Language (HQL)
Query query = session.createQuery(”from Users where username =:username and password =:password”; query.setParameter(”username”, username); query.setParameter(”password”, password);ตัวอย่างการใช้ Stored Procedure เพื่อส่งผ่าน parameterized query
String username = request.getParameter(”username”); String password = request.getParameter(”password”); try { CallableStatement cs = conn.prepareCall(”{call getUsers(?, ?)}”); cs.setString(1, username); cs.setString(2, password); ResultSet results = cs.executeQuery(); }เทคนิค Escape อีกทางเลือกหนึ่งกรณีที่เราไม่ใช้ Parameterized query และส่งผลต่อ ประสิทธิภาพ ถ้าใช้งานอย่างไม่เหมาะสม เพื่อสร้าง dynamic query ลองเลือกเทคนิค Escape (เช่นเดียวกับ Java ที่มี character escaping) แต่อย่างไรก็ตามเทคนิคนี้ค่อนข้างเปราะบางกว่าเมื่อเทียบกับ parameterized query อ่านรายละเอียด character escaping ของ DBMS แต่ละเจ้าได้ดังนี้ศึกษาตัวอย่าง escape ได้จากโค้ด OracleCodec class ของ owasp-esapi
//encode ' to '' public String encodeCharacter( char[] immune, Character c ) { if ( c.charValue() == '\'' ) return "\'\'"; return ""+c; }Note: การเลือกใช้ PreparedStatement หรือ Statement และการเพิ่มประสิทธิภาพการทำงาน เพิ่มเติม: Review Code for SQL Injection Test for SQL Injection Reference: SQL injection
No comments:
Post a Comment