SQL隱碼攻擊問題以及解決方法

松鼠先生_發表於2020-11-11

sql注入

       SQL Injection:就是通過把SQL命令插入到Web表單遞交或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令。具體來說,它是利用現有應用程式,將(惡意)的SQL命令注入到後臺資料庫引擎執行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個存在安全漏洞的網站上的資料庫,而不是按照設計者意圖去執行SQL語句。

-- 程式碼中的SQL語句

   "SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';";

-- 將使用者輸入的賬號aaa和密碼:a' or '1'='1拼接後

    "SELECT * FROM user WHERE name='aaa' AND password='a' or '1'='1';"

我們讓使用者輸入的密碼和SQL語句進行字串拼接。使用者輸入的內容作為了SQL語句語法的一部分,改變了原有SQL真正的意義,以上問題稱為SQL隱碼攻擊。 要解決SQL隱碼攻擊就不能讓使用者輸入的密碼和我們的SQL語句進行簡單的字串拼接。

 

防止sql注入的解決方法:PreparedSatement預編譯物件

(1)PreparedSatement的執行原理

我們寫的SQL語句讓資料庫執行,資料庫不是直接執行SQL語句字串。和Java一樣,資料庫需要執行編譯後的SQL語句(類似Java編譯後的位元組碼檔案)。

1.statement物件每執行一條SQL語句都會先將這條SQL語句傳送給資料庫編譯,資料庫再執行

Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO users VALUES (1, '張三', '123456');");
stmt.executeUpdate("INSERT INTO users VALUES (2, '李四', '666666');");

上面2條SQL語句我們可以看到大部分內容是相同的,只是資料略有不一樣。資料庫每次執行都編譯一次。如果有1萬條類似的SQL語句,資料庫需要編譯1萬次,執行1萬次,顯然效率就低了。

public class JdbcPrecompile {
    public static void main(String[] args) {
        Connection conn =null;
        PreparedStatement preparedStatement =null;
        try {
            conn = JdbcUtilDemo02.getConnection();
            String sql="insert into user(name,username,password) values (?,?,?)";
            //預編譯sql語句
            preparedStatement = conn.prepareStatement(sql);

            //設定sql語句中的引數
            preparedStatement.setString(1,"張三");
            preparedStatement.setString(2,"spsglz");
            preparedStatement.setString(3,"123456");
            preparedStatement.executeUpdate();
            
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
           //關流
        }
    }
}

prepareStatement()會先將SQL語句傳送給資料庫預編譯。PreparedStatement會引用著預編譯後的結果。可以多次傳入不同的引數給PreparedStatement物件並執行。相當於呼叫方法多次傳入不同的引數。

(2)PreparedSatement的好處

1.prepareStatement()先將SQL語句傳送給資料庫預編譯。PreparedStatement會引用著預編譯後的結果。可以多次傳入不同的引數給PreparedStatement物件並執行。減少SQL編譯次數,提高效率。

2.安全性更高,沒有SQL隱碼攻擊的隱患。

3.提高了程式的可讀性

(3)PreparedSatement的基本使用

PreparedStatement prepareStatement(String sql)  
//會先將SQL語句傳送給資料庫預編譯。PreparedStatement物件會引用著預編譯後的結果。
void setDouble(int parameterIndex, double x) 
//將指定引數設定為給定 Java double 值。
void setFloat(int parameterIndex, float x) 
//將指定引數設定為給定 Java REAL 值。
void setInt(int parameterIndex, int x) 
//將指定引數設定為給定 Java int 值。
void setLong(int parameterIndex, long x) 
//將指定引數設定為給定 Java long 值。
void setObject(int parameterIndex, Object x) 
//使用給定物件設定指定引數的值。  
void setString(int parameterIndex, String x) 
//將指定引數設定為給定 Java String 值。 
ResultSet executeQuery() 
//在此 PreparedStatement 物件中執行 SQL 查詢,並返回該查詢生成的ResultSet物件。 
int executeUpdate() 
//在此 PreparedStatement 物件中執行 SQL 語句,該語句必須是一個 SQL 資料操作語言DML語句
//比如 INSERT、UPDATE 或 DELETE 語句;或者是無返回內容的 SQL 語句,比如 DDL 語句。 

(4)PreparedSatement使用步驟

    1. 編寫SQL語句,未知內容使用?佔位:"SELECT * FROM user WHERE name=? AND password=?;";
    2. 獲得PreparedStatement物件
    3. 設定實際引數
    4. 執行引數化SQL語句
    5. 關閉資源

相關文章