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注入雖然十幾年前就被淘汰了,但是現在仍然有一些程式員偷懶,粗心大意,寫了一些帶有漏洞的代碼,讓黑客有機可乘,顯得程式猿很不專業,奉勸在座的各位,耗子喂汁,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/230760.html
標籤:其他
