為什麼用Prepared Statement還會有SQL injection?

為什麼用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=" + username + " 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]

 

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *