JDBC:使用Statement引發SQL注入
阿新 • • 發佈:2020-12-06
1. 什麼是 SQL 注入?
jdbc程式執行時, sql語句在拼接時由頁面傳入引數,如果使用者惡意傳入一些sql中的特殊關鍵字,會導致sql語句意義發生變化,這種攻擊方式就叫做sql注入。
2. 引子:
sql注入的危害: 黑客可以一行程式碼登入超管賬戶,對資料庫造成不可挽回的損失。
參考使用者註冊登入案例:
// 使用者登入驗證(字串拼接)
String sql = "select * from s_user where loginName = '"+ loginName +"' and loginPwd = '"+ loginPwd +"'";
// 正常使用者: 使用者名稱:admin 密 碼:123456 可以正常登入 --------------------------------------------- // 惡意使用者: 使用者名稱: aaa 密 碼: aaa' or '1'='1 也可以登陸成功, 這叫做SQL注入,
由於 惡意使用者 輸入的密碼被當作sql語句,編譯時,1=1,返回true, 所以驗證通過,登陸成功。
3. 具體例項
下方的登入例項,執行成功後,後臺輸入惡意sql語句,就可以登陸成功。
public class JDBCTest02 { public static void main(String[] args) { // 初始化介面,(返回使用者名稱/密碼) Map<String,String> userLoginInfo = initUi(); // 驗證使用者名稱和密碼:(傳入登入資訊) boolean loginSuccess = login(userLoginInfo); // 登陸成功/失敗 ==> 布林值 // 最後輸出結果 System.out.println(loginSuccess ? "登陸成功": "登陸失敗"); } /* * 使用者登入: * @param userLoginInfo 使用者登入資訊 * @return false 登陸失敗, true 登陸成功 */ private static boolean login(Map<String, String> userLoginInfo) { // JDBC 程式碼 Connection conn = null; Statement stmt = null; ResultSet rs = null; // 單獨定義變數 String loginName = userLoginInfo.get("loginName"); String loginPwd = userLoginInfo.get("loginPwd"); // 打標記意識 boolean loginSuccess = false; try { // 1. 註冊驅動 Class.forName("com.mysql.jdbc.Driver"); // 2. 獲取資料庫連線 // 使用時,把school改為你自己的資料庫名 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/school", "root","root"); // 3. 獲取資料庫操作物件 stmt = conn.createStatement(); // 4. 執行SQL // 注意: s_user是school裡的使用者表,記得改為你自己定義的的使用者表!! String sql = "select * from s_user where loginName = '"+ loginName +"' and loginPwd = '"+ loginPwd +"'"; rs = stmt.executeQuery(sql); // 5. 處理結果集 if (rs.next()){ // 登陸成功 loginSuccess = true; } } catch (Exception e) { e.printStackTrace(); }finally { // 6. 釋放資源 if (rs !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } // return false; return loginSuccess; } /* * 初始化使用者介面 * @return 使用者輸入的使用者名稱和密碼等登入資訊 * */ private static Map<String, String> initUi() { Scanner s = new Scanner(System.in); System.out.println("使用者名稱: "); String loginName = s.nextLine(); System.out.println("密碼: "); String loginPwd = s.nextLine(); // 用鍵值對儲存輸入資訊 Map<String,String> userLoginInfo = new HashMap<>(); // 傳值 userLoginInfo.put("loginName", loginName); userLoginInfo.put("loginPwd", loginPwd); return userLoginInfo; } }
4. 如何防止sql注入?
原理: 讓使用者輸入資訊不參與SQL語句的編譯過程,問題就解決了。
即使使用者提供的資訊中含有SQL語句的關鍵字,但是無法參與編譯,也不起作用。
這裡引入 Statement 和 PreparedStatement
- 上面的案例使用了資料庫操作物件 Statement ,讓sql語句直接參與編譯。
- SUN公司發明了 PreparedStatement 來讓sql語句預編譯,然後再傳值,間接的阻止了惡意的sql注入。
5. PreperedStatement 相對 Statement 的優點:
- 沒有SQL注入的問題。
- Statement會使資料庫頻繁編譯SQL,可能造成資料庫緩衝區溢位。
- 資料庫和驅動可以對PreperedStatement進行優化(只有在相關聯的資料庫連線沒有關閉的情況下有效)。
6. 總結
SQL注入雖然十幾年前就被淘汰了,但是現在仍然有一些程式設計師偷懶,粗心大意,寫了一些帶有漏洞的程式碼,讓黑客有機可乘,顯得程式猿很不專業,奉勸在座的各位,耗子喂汁。