Oto SQL do skonfigurowania przykładu:
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);
Klasa Inject jest podatna na iniekcje SQL. Zapytanie jest dynamicznie wklejane wraz z danymi wejściowymi użytkownika. Celem zapytania było pokazanie informacji o Robercie. Wynagrodzenie lub premia na podstawie danych wejściowych użytkownika. Jednak złośliwy użytkownik manipuluje danymi wejściowymi, uszkadzając zapytanie, dołączając odpowiednik „lub prawda” do klauzuli where, tak aby wszystko zostało zwrócone, w tym informacje o Aaronie, które miały być ukryte.
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"));
}
}
}
Po uruchomieniu tego pierwszy przypadek dotyczy normalnego użytkowania, a drugi złośliwego wstrzyknięcia:
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
Nie należy budować instrukcji SQL z konkatenacją łańcuchów danych wejściowych użytkownika. Jest nie tylko podatny na wstrzyknięcie, ale ma również wpływ na buforowanie na serwerze (instrukcja zmienia się, więc prawdopodobieństwo trafienia w pamięć podręczną instrukcji SQL jest mniejsze, podczas gdy przykład wiązania zawsze uruchamia tę samą instrukcję).
Oto przykład Binding, aby uniknąć tego rodzaju zastrzyków:
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"));
}
}
}
Uruchomienie tego z tymi samymi danymi wejściowymi, co w poprzednim przykładzie, pokazuje, że złośliwy kod nie działa, ponieważ żaden typ płatności nie pasuje do tego ciągu:
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=?