為什麼用Prepared Statement還會有SQL injection?
這篇文章主要打破一個迷思, 不是使用 prepare Statement就可以避免SQL injection
正點是要正確地使用, 才能夠避免 SQL injection!!
因此, 我們會舉一個錯誤使用 prepare Statement 的程式範例與正確使用的程式範例說明
錯誤的使用 Prepare Statement
這個程式範例雖然最後使用 prepareStatement 執行SQL語句
但是整個 SQL 語句還是透過字串的方式組合完成, usernmae 與 pwd 的參數輸入直接與 SQL 語句字串結合
這樣的方式雖然使用 prepareStatement執行, 但是還是會有 SQL injection
String sqlString =
"select * from db_user where username="
+ u
sername +
" and password ="
+ pwd;
PreparedStatement stmt = connection.prepareStatement(sqlString);
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
class Login {
public Connection getConnection() throws SQLException {
DriverManager.registerDriver(new
com.microsoft.sqlserver.jdbc.SQLServerDriver());
String dbConnection =
PropertyManager.getProperty("db.connection");
// Can hold some value like
// "jdbc:microsoft:sqlserver://<HOST>:1433,<UID>,<PWD>"
return DriverManager.getConnection(dbConnection);
}
String hashPassword(char[] password) {
// Create hash of password
}
public void doPrivilegedAction(
String username, char[] password
) throws SQLException {
Connection connection = getConnection();
if (connection == null) {
// Handle error
}
try {
String pwd = hashPassword(password);
String sqlString = "select * from db_user where username=" +
username + " and password =" + pwd;
PreparedStatement stmt = connection.prepareStatement(sqlString);
ResultSet rs = stmt.executeQuery();
if (!rs.next()) {
throw new SecurityException("User name or password incorrect");
}
// Authenticated; proceed
} finally {
try {
connection.close();
} catch (SQLException x) {
// Forward to handler
}
}
}
}
[/pastacode]
正確使用 Prepare Statement
那麼要怎樣正確的使用 prepare Statement呢? 透過輸入參數畫
例如下列範例, username, pwd 是兩個輸入參數
因此在進行 SQL 執行時, username 與 pwd 永遠只會被當作參數來處理,
而不會變成整個 SQL 語句中的一部分
這樣才能夠有效防止 SQL injection.
最後提醒的是, 永遠對於使用者資料輸入進行字元有效性檢查
String sqlString =
"select * from db_user where username=? and password=?"
;
PreparedStatement stmt = connection.prepareStatement(sqlString);
stmt.setString(
1
, username);
stmt.setString(
2
, pwd);
ResultSet rs = stmt.executeQuery();
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
public void doPrivilegedAction(
String username, char[] password
) throws SQLException {
Connection connection = getConnection();
if (connection == null) {
// Handle error
}
try {
String pwd = hashPassword(password);
// Validate username length
if (username.length() > 8) {
// Handle error
}
String sqlString =
"select * from db_user where username=? and password=?";
PreparedStatement stmt = connection.prepareStatement(sqlString);
stmt.setString(1, username);
stmt.setString(2, pwd);
ResultSet rs = stmt.executeQuery();
if (!rs.next()) {
throw new SecurityException("User name or password incorrect");
}
// Authenticated; proceed
} finally {
try {
connection.close();
} catch (SQLException x) {
// Forward to handler
}
}
}
[/pastacode]