Java開發筆記(一百四十九)引入預報告的好處

前面介紹了各類SQL語句的調用過程,雖然例子代碼寫死了每一個SQL串,可是徹底能夠把查詢條件做爲方法參數傳進來。好比如今想刪除某個課程的教師記錄,那麼在編寫刪除方法時,就把課程名稱做爲該方法的一個輸入參數。據此編寫的方法代碼示例以下:html

	// 刪除記錄
	private static void deleteRecord(Statement stmt, String course) throws SQLException {
		String sql = String.format("delete from teacher where course='%s'", course);
		int count = stmt.executeUpdate(sql); // 執行處理語句
		System.out.println("待執行的SQL語句:"+sql);
		System.out.println("刪除記錄語句的返回結果爲"+count);
	}

 

接着外部準備調用上面的deleteRecord方法,第二個課程參數填「化學」,表示但願刪除全部化學老師的記錄,調用代碼以下所示:mysql

			deleteRecord(stmt, "化學"); // 刪除記錄

 

運行包含以上代碼的測試程序,觀察到如下的輸出日誌。sql

待執行的SQL語句:delete from teacher where course='化學'
刪除記錄語句的返回結果爲1

從日誌信息可見,只刪除一條化學老師的記錄,看起來彷佛一切正常。不過課程參數由外部傳入,誰知道課程字符串是什麼東西呢?假若有人閒得發慌,在鍵盤上隨便輸了幾個字符,像「' or '1'='1」這樣的字符串看成課程名稱。因而刪除方法的調用代碼變成了下面這般:數據庫

			deleteRecord(stmt, "' or '1'='1"); // 刪除記錄

 

再次運行測試程序,發現輸出日誌變得有點不對勁:安全

待執行的SQL語句:delete from teacher where course='' or '1'='1'
刪除記錄語句的返回結果爲4

 

沒想到隨便輸的幾個字符居然也讓SQL語句執行了,並且是把teacher表的剩餘記錄刪得精光。這可不得了了,原語句的格式明明只肯刪除特定課程的記錄,爲啥執行結果截然不同呢?原因在於待執行的SQL語句呆板地將課程字符串原樣填了進去,形成出現「or '1'='1'」這種極端條件,天然MySQL忠實地刪光了teacher表。諸如此類的SQL缺陷,人稱SQL注入漏洞,它經常被黑客利用隨心所欲,形成重大損失。
上述的實驗結果暴露了報告機制的安全問題,一旦條件參數被人惡意篡改,就可能產生意料以外的嚴重情況。爲此JDBC又設計了另外一種預報告機制,預報告定義了新類PreparedStatement,與原報告Statement不一樣的是,建立預報告對象時就要設定SQL語句,而且SQL裏面的動態參數以問號代替。而後準備調用executeUpdate或者executeQuery以前,先調用預報告對象的setString方法來設置對應序號的參數值。下面即是引入預報告以後的數據庫操做代碼例子:測試

	// 測試預報告的處理
	private static void testPreparedStatement() {
		String sql = "delete from teacher where course=?";
		// 先獲取數據庫的鏈接,再建立鏈接的預報告
		try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
				PreparedStatement stmt = conn.prepareStatement(sql)) {
			//stmt.setString(1, "化學"); // 設置對應序號的參數值
			stmt.setString(1, "'' or 1=1"); // 設置對應序號的參數值
			int count = stmt.executeUpdate(); // 執行處理語句
			System.out.println("預先準備的SQL語句:"+stmt.toString());
			System.out.println("刪除記錄語句的返回結果爲"+count);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

 

仍以以前的惡意字符串爲例,上面代碼在調用setString方法時填入了「' or '1'='1」,意圖繼續渾水摸魚。運行包含testPreparedStatement方法的測試程序,觀察到的日誌信息以下所示:設計

預先準備的SQL語句:com.mysql.cj.jdbc.ClientPreparedStatement: delete from teacher where course='\'\' or 1=1'
刪除記錄語句的返回結果爲0

 

從日誌結果可見,此次搗亂行爲沒有得逞,一條記錄都沒刪除。注意此時的條件語句變爲「course='\'\' or 1=1'」,顯然預報告把字符串中的單引號作了轉義,使得轉義後的條件語句格式不正確,也就沒能成功執行SQL。由此證實預報告PreparedStatement提高了數據庫操做的安全性,凡是須要動態傳入參數的SQL語句,最好採起預報告機制加以處理。日誌



更多Java技術文章參見《Java開發筆記(序)章節目錄orm

相關文章
相關標籤/搜索