一其弊、注入攻擊
在用戶登錄的時(shí)候癞己,我們往往需要輸入賬號(hào)和密碼,通過(guò)賬號(hào)和密碼和數(shù)據(jù)庫(kù)中保存的賬號(hào)密碼進(jìn)行匹配梭伐,匹配成功則登錄成功痹雅,但是在匹配的時(shí)候會(huì)存在注入攻擊的安全隱患,在輸入賬號(hào)和密碼的時(shí)候糊识,在末尾加上?"or" 再接上任何為真的語(yǔ)句绩社,這樣一來(lái),有真就為真赂苗,這樣也能登錄成功愉耙。
現(xiàn)有 mylogon 數(shù)據(jù)庫(kù),里面有 users 數(shù)據(jù)表拌滋,存儲(chǔ)了賬號(hào)和密碼朴沿,使用Java通過(guò)JDBC操作數(shù)據(jù)庫(kù)來(lái)模擬一下注入攻擊:
public static void main(String[] args) throws ClassNotFoundException, SQLException {
? ? //1.注冊(cè)驅(qū)動(dòng) 反射技術(shù),將驅(qū)動(dòng)類加入到內(nèi)容
? ? Class.forName("com.mysql.jdbc.Driver");
? ? //2.獲得數(shù)據(jù)庫(kù)連接? DriverManager類中靜態(tài)方法
? ? //static Connection getConnection(String url, String user, String password)
? ? //返回值是Connection接口的實(shí)現(xiàn)類,在mysql驅(qū)動(dòng)程序
? ? //url: 數(shù)據(jù)庫(kù)地址? jdbc:mysql://連接主機(jī)IP:端口號(hào)//數(shù)據(jù)庫(kù)名字
? ? String url =? "jdbc:mysql://localhost:3306/mylogon";
? ? String username = "root";
? ? String password = "123456";
? ? Connection con = DriverManager.getConnection(url,username,password);
? ? //3.獲得語(yǔ)句執(zhí)行平臺(tái)
? ? //Statement createStatement() 獲取Statement對(duì)象,將SQL語(yǔ)句發(fā)送到數(shù)據(jù)庫(kù)
? ? Statement stat = con.createStatement();
? ? //4.從控制臺(tái)輸入用戶名和密碼
? ? Scanner sc = new Scanner(System.in);
? ? String user = sc.nextLine();
? ? String key = sc.nextLine();
? ? //String sql1 = "select * from users where username='qwer' and password='1234535' or 1=1";
? ? String sql2 = "select * from users where username='"+ user +"'and password='"+ key +"'";
? ? System.out.println(sql2);
? ? ResultSet res = stat.executeQuery(sql2);
? ? while (res.next())
? ? {
? ? ? ? System.out.println(res.getString("username") + "? ? " + res.getString("password"));
? ? }
? ? con.close();
? ? res.close();
? ? stat.close();
}
當(dāng)從控制臺(tái)輸入正確的賬號(hào)和密碼時(shí),可以登錄成功败砂,但當(dāng)輸入任意賬號(hào)和密碼后赌渣,在密碼后面加上 'or' 1=1,也能夠登錄成功昌犹,這樣就存在安全隱患
二坚芜、注入攻擊解決方案
可以使用 PrearedStatement 來(lái)解決注入攻擊的問(wèn)題,在Statement 接口的子接口中斜姥,有一個(gè) PrearedStatement 接口货岭,可以使 SQL 預(yù)編譯存儲(chǔ),多次高效地執(zhí)行 SQL疾渴,并能防止注入攻擊千贯。使用PreparedStatement pst = ?con.prepareStatement(sql):調(diào)用Connection接口的方法prepareStatement,獲取PrepareStatement接口的實(shí)現(xiàn)類
執(zhí)行SQL語(yǔ)句,數(shù)據(jù)表,查詢用戶名和密碼,如果存在,登錄成功,不存在登錄失敗
調(diào)用Connection接口的方法prepareStatement,獲取PrepareStatement接口的實(shí)現(xiàn)類
調(diào)用PreparedStatement對(duì)象set方法,設(shè)置問(wèn)號(hào)占位符上的參數(shù)
調(diào)用方法,執(zhí)行SQL,獲取結(jié)果集
public static void main(String[] args) throws SQLException, ClassNotFoundException {
? ? //1.注冊(cè)驅(qū)動(dòng) 反射技術(shù),將驅(qū)動(dòng)類加入到內(nèi)容
? ? Class.forName("com.mysql.jdbc.Driver");
? ? //2.獲得數(shù)據(jù)庫(kù)連接? DriverManager類中靜態(tài)方法
? ? //static Connection getConnection(String url, String user, String password)
? ? //返回值是Connection接口的實(shí)現(xiàn)類,在mysql驅(qū)動(dòng)程序
? ? //url: 數(shù)據(jù)庫(kù)地址? jdbc:mysql://連接主機(jī)IP:端口號(hào)//數(shù)據(jù)庫(kù)名字
? ? String url =? "jdbc:mysql://localhost:3306/mylogon";
? ? String username = "root";
? ? String password = "123456";
? ? Connection con = DriverManager.getConnection(url,username,password);
? ? //3.從控制臺(tái)輸入用戶名和密碼
? ? Scanner sc = new Scanner(System.in);
? ? String user = sc.nextLine();
? ? String key = sc.nextLine();
? ? //執(zhí)行SQL語(yǔ)句,數(shù)據(jù)表,查詢用戶名和密碼,如果存在,登錄成功,不存在登錄失敗
? ? String sql2 = "SELECT * FROM users WHERE username= ? AND PASSWORD= ?";
? ? //4、調(diào)用Connection接口的方法prepareStatement,獲取PrepareStatement接口的實(shí)現(xiàn)類
? ? //方法中參數(shù),SQL語(yǔ)句中的參數(shù)全部采用問(wèn)號(hào)占位符
? ? PreparedStatement pre = con.prepareStatement(sql2);
? ? System.out.println(sql2);
? ? //5.調(diào)用pst對(duì)象set方法,設(shè)置問(wèn)號(hào)占位符上的參數(shù)
? ? pre.setObject(1,user);
? ? pre.setObject(2,key);
? ? //6.調(diào)用方法,執(zhí)行SQL,獲取結(jié)果集
? ? ResultSet res = pre.executeQuery();
? ? while (res.next())
? ? {
? ? ? ? System.out.println(res.getString("username") + "? ? " + res.getString("password"));
? ? }
? ? con.close();
? ? res.close();
? ? pre.close();
}