นี่คือ SQL สำหรับการตั้งค่าตัวอย่าง:
CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);
INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);
คลาส Inject มีความเสี่ยงต่อการฉีด SQL แบบสอบถามจะถูกวางแบบไดนามิกพร้อมกับอินพุตของผู้ใช้ จุดประสงค์ของแบบสอบถามคือเพื่อแสดงข้อมูลเกี่ยวกับ Bob เงินเดือนหรือโบนัสขึ้นอยู่กับการป้อนข้อมูลของผู้ใช้ แต่ผู้ใช้ที่เป็นอันตรายจะจัดการอินพุตที่ทำให้เคียวรีเกิดความเสียหายโดยการตรึงค่าเท่ากับ 'หรือจริง' กับส่วนคำสั่งที่ทำให้ทุกอย่างถูกส่งคืนรวมถึงข้อมูลเกี่ยวกับแอรอนที่ควรจะถูกซ่อนไว้
import java.sql.*;
public class Inject {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
System.out.println(sql);
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
การดำเนินการนี้กรณีแรกคือการใช้งานปกติและกรณีที่สองเป็นการฉีดที่เป็นอันตราย
c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50
c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0
คุณไม่ควรสร้างคำสั่ง SQL ของคุณด้วยการต่อสตริงเข้ากับผู้ใช้ ไม่เพียง แต่จะมีความเสี่ยงต่อการฉีดเท่านั้น แต่ยังมีผลกระทบต่อการแคชบนเซิร์ฟเวอร์ด้วย (คำสั่งเปลี่ยนไปดังนั้นมีโอกาสน้อยกว่าที่จะได้รับการตีคำสั่ง SQL ในขณะที่ตัวอย่างการเชื่อมโยงใช้คำสั่งเดียวกันเสมอ)
นี่คือตัวอย่างของการผูกเพื่อหลีกเลี่ยงการฉีดชนิดนี้:
import java.sql.*;
public class Bind {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
Connection conn = DriverManager.getConnection(url);
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
System.out.println(sql);
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, args[0]);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
การเรียกใช้สิ่งนี้ด้วยอินพุตเดียวกันกับตัวอย่างก่อนหน้านี้แสดงให้เห็นว่ารหัสที่เป็นอันตรายไม่ทำงานเพราะไม่มีประเภทการชำระเงินที่ตรงกับสตริงนั้น:
c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50
c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?