SQL注入問題並解決
阿新 • • 發佈:2020-12-02
SQL注入問題
package com.etc; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Scanner; /* 實現功能 1.需求:模擬使用者登入實現功能 2.業務描述: 程式執行的時候 提供一個輸入的入口 可以讓使用者輸入使用者名稱和密碼 使用者輸入使用者名稱和密碼之後 提交資訊 java程式收集到使用者資訊 Java程式連線資料庫驗證使用者名稱和密碼是否合法 合法:顯示登陸成功 不合法:顯示登陸失敗 3.資料的準備: 在實際開發中 表的設計會使用一個專業的建模工具 PowerDesigner 使用PD工具進行資料庫表的設計 4.當前程式存在的問題: 請輸入使用者:zhangsan 請輸入密碼:zhangsan'or'1=1 登入成功 這種現象被稱為sql注入 5.導致SQL注入的根本原因 使用者輸入的資訊中含有sql語句的關鍵字 並且這些關鍵字參與sql語句的編譯過程 導致sql語句的原意被扭曲 進而達到sql注入 */ public class JdbcTest03{ public static void main(String[] args) { // 初始化介面 Map<String,String> userLoginInfo = initUI(); // 驗證使用者名稱和密碼 boolean loginSuccess = login(userLoginInfo); // 輸出最後結果 System.out.println(loginSuccess ? "登入成功" : "登入失敗"); } /** * 使用者登入 * @param userLoginInfo 使用者登入資訊 * @return true表示登入成功,false表示登入失敗 */ @SuppressWarnings("all") private static boolean login(Map<String, String> userLoginInfo) { //打標機 boolean loginSuccess = false; Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 1、註冊驅動 Class.forName("com.mysql.jdbc.Driver"); // 2、獲取連線 conn = DriverManager.getConnection("jdbc:mysql://localhost:3307/test","root","root"); // 3、獲取資料庫操作物件 stmt = conn.createStatement(); // 4、執行sql語句 String sql = "select * from t_user where loginName = '"+ userLoginInfo.get("userName")+ "' and loginPwd = '" + userLoginInfo.get("userPassword")+ "'"; //以上正好完成了sql語句的拼接 以下程式碼的含義是 傳送sql語句給DBMS DBMS進行sql編譯 //正好將使用者提供的‘非法資訊’編譯進去 導致了sql語句含義被扭曲 rs = stmt.executeQuery(sql); // 5、處理結果集 if(rs.next()) { loginSuccess = true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { // 6、釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } return loginSuccess; } /** * 初試化介面 * @return 使用者輸入的使用者名稱和密碼等登入資訊 */ private static Map<String, String> initUI() { Scanner s = new Scanner(System.in); System.out.print("請輸入使用者:"); String userName = s.nextLine(); System.out.print("請輸入密碼:"); String userPassword = s.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("userName",userName); userLoginInfo.put("userPassword",userPassword); return userLoginInfo; } }
解決sql注入問題
package com.etc; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Scanner; /* 1.解決SQL注入問題: 只要使用者提供的資訊不參與sql語句的編譯過程 問題就解決了 要想使用者資訊不參與SQL語句的編譯 那麼必須使用java.sql.PerparedStatement PerparedStatement介面繼承了java.sql.Statement PerparedStatement是屬於預編譯的資料庫操作物件 PerparedStatement的原理是 預先對SQL語句的框架進行編譯 然後再給SQL語句傳‘值’ 2.測試 請輸入使用者:chen 請輸入密碼:chen'or'1=1 登入失敗 3.解決sql注入的關鍵是 使用者提供的資訊中即使含有sql語句的關鍵字 但是這些關鍵字並沒有參與編譯 不起作用 */ public class JdbcTest05 { public static void main(String[] args) { // 初始化介面 Map<String,String> userLoginInfo = initUI(); // 驗證使用者名稱和密碼 boolean loginSuccess = login(userLoginInfo); // 輸出最後結果 System.out.println(loginSuccess ? "登入成功" : "登入失敗"); } /** * 使用者登入 * @param userLoginInfo 使用者登入資訊 * @return true表示登入成功,false表示登入失敗 */ @SuppressWarnings("all") private static boolean login(Map<String, String> userLoginInfo) { //打標機 boolean loginSuccess = false; String loginName = userLoginInfo.get("loginName"); String loginPwd = userLoginInfo.get("loginPwd"); Connection conn = null; PreparedStatement ps = null;//這裡使用PerparedStatement(預編譯的資料庫操作物件) ResultSet rs = null; try { // 1、註冊驅動 Class.forName("com.mysql.jdbc.Driver"); // 2、獲取連線 conn = DriverManager.getConnection("jdbc:mysql://localhost:3307/test","root","root"); // 3、獲取資料庫操作物件 //其中一個問號代表一個佔位符 一個問號將來接收一個“值”,注意:佔位符不能使用單引號括起來 String sql = "select * from t_user where loginName = ? and loginPwd = ?";//SQL語句的框架 //程式執行到此處 會發送sql語句框子給DBMS 然後DBMS進行sql語句的預先編譯 ps = conn.prepareStatement(sql); //給佔位符?傳值(第一個問號下標是1 第二個問號下標是2 JDBC中所有下標從1開始) ps.setString(1,loginName); ps.setString(2,loginPwd); // 4、執行sql語句 rs = ps.executeQuery(); // 5、處理結果集 if(rs.next()) { loginSuccess = true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { // 6、釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } return loginSuccess; } /** * 初試化介面 * @return 使用者輸入的使用者名稱和密碼等登入資訊 */ @SuppressWarnings("all") private static Map<String, String> initUI() { Scanner s = new Scanner(System.in); System.out.print("請輸入使用者:"); String userName = s.nextLine(); System.out.print("請輸入密碼:"); String userPassword = s.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",userName); userLoginInfo.put("loginPwd",userPassword); return userLoginInfo; } }
對比Statement和preparedStatement
4.對比Statement和PreparedStatement - Statementz存在注入問題 PreparedStatement解決了注入問題 - Statement是編譯一次執行一次 PreparedStatement是編譯一次 可執行N次 PreparedStatement效率較高一些 - preparedStatement會在編譯階段做型別的安全檢查 5.什麼情況下必須使用Statement? 業務方面要求必須支援SQL注入現象 Statement支援SQL注入 凡是業務方面要求是需要進行sql語句拼接的 必須使用Statement