JDBC3——SQL隱碼攻擊、及其解決方法——Statement與PreparedStatement對比——PreparedStatement的CRUD
1.使用者登陸系統演示—Statement—SQL隱碼攻擊
1.1.需求:
-
模擬使用者登陸功能的實現
- 使用者名稱:zhangsan
- 密碼:123
1.2.業務描述:
-
程式執行的時候,提供一個輸入的入口,可以讓使用者輸入使用者名稱、密碼 輸入後,提交資訊,java程式蒐集到使用者資訊
-
JAVA程式連線資料庫驗證是否合法 合法:顯示登陸成功 非法:顯示登陸失敗
1.3.本程式存在的問題—— 存在SQL語句注入現象
- 使用者名稱:a
- 密碼:b’ or ‘1’='1
- 會登入成功
1.4.導致SQL語句注入的根本原因:
- 使用者輸入的東西,含有sql關鍵字,而且編譯進去了,導致系統原來的意思被扭曲!!!!
1.5.解決方法:
- 只要使用者輸入的SQL關鍵字不編譯進去就可以了
1.6.執行結果 - 正確例項:
- 錯誤結果:
public class JDBCTestSqlInject {
public static void main(String[] args) {
//1.輸入資訊
Map<String, String> userLoginInfo = inintUI();
//2.驗證資訊是否正確
boolean loginSuccess = login(userLoginInfo);
System.out.println(loginSuccess ? "登陸成功" : "登陸失敗");
}
/**
* 驗證使用者名稱,密碼是否正確
* @param userLoginInfo
* @return
*/
private static boolean login (Map < String, String > userLoginInfo){
boolean flag=false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
//JDBC程式碼
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.註冊驅動(作用:告訴java程式,即將要連線哪個品牌的資料庫)
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連線(表示JVM的程式和資料庫程式之間的 通道 開啟了,使用完之後 必須關閉)
String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址連結
String user = "root";
String password = "333";
conn = DriverManager.getConnection(url,user,password);
//3.獲取資料庫操作物件(專門執行sql語句的物件)
stmt = conn.createStatement();
//4.執行sql語句(主要執行DQL、DML……)
String sql = " select * from t_user where loginName ='"+ loginName +"'and loginPwd ='"+ loginPwd +"'";
System.out.println(sql);
//5.處理查詢結果集(只有當第4步執行的是select語句時,才有這第5步)
rs = stmt.executeQuery(sql); //專門執行DQL查詢語句
if(rs.next()){
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.釋放資源(使用完資源後一定要關閉資源,java和資料庫屬於程式間通訊,開啟後一定要關閉)
try{
if(stmt != null)
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return flag;
}
/**
* 初始化使用者介面
* @return 使用者輸入使用者名稱、密碼等資訊
*/
private static Map<String, String> inintUI () {
Scanner s = new Scanner(System.in);
System.out.print("使用者名稱:");
String loginName = s.nextLine();
System.out.print("密碼:");
String loginPwd = s.nextLine();
Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
2.SQL隱碼攻擊的解決——PreparedStatement
2.1.改動1——Statement改為PreparedStatement
-
// Statement stmt = null; PreparedStatement stmt = null;
2.2.改動:2——改為佔位符—?
-
//String sql = " select * from t_user where loginName ='"+ loginName +"'and loginPwd ='"+ loginPwd +"'"; //一個問號表示一個佔位符 String sql = " select * from t_user where loginName = ? and loginPwd = ? ";
2.3.改動:3——prepareStatement進行預編譯
-
// stmt = conn.createStatement(); //程式執行到此,會傳送sql語句”框子“給DBMS,然後它進行‘預編譯 stmt = conn.prepareStatement(sql);
2.4.改動:4—— 給佔位符傳值
-
stmt.setString(1,loginName); stmt.setString(2,loginPwd);
public class JDBCTestSqlInjectSolve {
public static void main(String[] args) {
//1.輸入資訊
Map<String, String> userLoginInfo = inintUI();
//2.驗證資訊是否正確
boolean loginSuccess = login(userLoginInfo);
System.out.println(loginSuccess ? "登陸成功" : "登陸失敗");
}
/**
* 驗證使用者名稱,密碼是否正確
* @param userLoginInfo
* @return
*/
private static boolean login (Map < String, String > userLoginInfo){
boolean flag=false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
//JDBC程式碼
Connection conn = null;
//改動1:
// Statement stmt = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
//1.註冊驅動(作用:告訴java程式,即將要連線哪個品牌的資料庫)
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連線(表示JVM的程式和資料庫程式之間的 通道 開啟了,使用完之後 必須關閉)
String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址連結
String user = "root";
String password = "333";
conn = DriverManager.getConnection(url,user,password);
//3.獲取資料庫操作物件(專門執行sql語句的物件)
//改動:2
// String sql = " select * from t_user where loginName ='"+ loginName +"'and loginPwd ='"+ loginPwd +"'";
//一個問號表示一個佔位符
String sql = " select * from t_user where loginName = ? and loginPwd = ? ";
//改動:3
// stmt = conn.createStatement();
//程式執行到此,會傳送sql語句”框子“給DBMS,然後它進行‘預編譯
stmt = conn.prepareStatement(sql);
//改動:4 給佔位符傳值
stmt.setString(1,loginName);
stmt.setString(2,loginPwd);
//4.執行sql語句(主要執行DQL、DML……)
rs = stmt.executeQuery(); //專門執行DQL查詢語句
//5.處理查詢結果集(只有當第4步執行的是select語句時,才有這第5步)
if(rs.next()){
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.釋放資源(使用完資源後一定要關閉資源,java和資料庫屬於程式間通訊,開啟後一定要關閉)
try{
if(stmt != null)
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return flag;
}
/**
* 初始化使用者介面
* @return 使用者輸入使用者名稱、密碼等資訊
*/
private static Map<String, String> inintUI () {
Scanner s = new Scanner(System.in);
System.out.print("使用者名稱:");
String loginName = s.nextLine();
System.out.print("密碼:");
String loginPwd = s.nextLine();
Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
3.對比Statement 與 PreparedStatement
3.1.區別:
-
statement存在SQL隱碼攻擊問題,PreparedStatement解決了SQL隱碼攻擊問題
-
Statement是編譯一次執行一次,PreparedStatement是編譯一次,可執行N次,效率較高
-
PreparedStatement會在編譯階段做型別的安全檢查
-
綜上:99%的情況使用PreparedStatement,1%的情況使用Statement
3.2.什麼情況使用Statement?
- 業務要求必須進行SQL隱碼攻擊時
- Statement支援注入,凡是要求需要進行sql語句拼接的,必須使用Statement
3.2.1. 比如::::把emp表內的資料按工資
- desc排列
- asc 排列
Statement實現:
public class JDBCStatementOnly {
public static void main(String[] args) {
//JDBC程式碼
Connection conn = null;
//改動1:
Statement stmt = null;
// PreparedStatement stmt = null;
ResultSet rs = null;
//排列方式:
Scanner s = new Scanner(System.in);
System.out.print("desc or asc ?:");
String order = s.nextLine();
try{
//1.註冊驅動(作用:告訴java程式,即將要連線哪個品牌的資料庫)
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連線(表示JVM的程式和資料庫程式之間的 通道 開啟了,使用完之後 必須關閉)
String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址連結
String user = "root";
String password = "333";
conn = DriverManager.getConnection(url,user,password);
//3.獲取資料庫操作物件(專門執行sql語句的物件)
stmt = conn.createStatement();
//4.執行sql語句(主要執行DQL、DML……)
String sql = " select * from emp order by sal "+order;
//5.處理查詢結果集(只有當第4步執行的是select語句時,才有這第5步)
rs = stmt.executeQuery(sql); //專門執行DQL查詢語句
boolean flag = rs.next();
while (flag){
//列表標籤
String empno = rs.getString("empno");
String ename = rs.getString("ename");
String sal = rs.getString("sal");
System.out.println(empno+","+ename+","+sal);
flag = rs.next();
}
}catch (Exception e){
e.printStackTrace();
}finally { //為保證一定關閉,放在finally關閉
//6.釋放資源(使用完資源後一定要關閉資源,java和資料庫屬於程式間通訊,開啟後一定要關閉)
//按照從小到大的順序關閉
try{
if(stmt != null)
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
preparedStatement 無法實現需求:
/***
* 把emp表內的資料按工資
* * desc排列
* * asc 排列
* preparedStatement 無法實現需求
*/
class JDBCpreparedStatementOnly {
public static void main(String[] args) {
//JDBC程式碼
Connection conn = null;
//改動1:
PreparedStatement stmt = null;
ResultSet rs = null;
//排列方式:
Scanner s = new Scanner(System.in);
System.out.print("desc or asc ?:");
String order = s.nextLine();
try{
//1.註冊驅動(作用:告訴java程式,即將要連線哪個品牌的資料庫)
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連線(表示JVM的程式和資料庫程式之間的 通道 開啟了,使用完之後 必須關閉)
String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址連結
String user = "root";
String password = "333";
conn = DriverManager.getConnection(url,user,password);
//3.獲取資料庫操作物件(專門執行sql語句的物件)
String sql = " select * from emp order by sal ? ";
stmt = conn.prepareStatement(sql);
//改動:4 給佔位符傳值
stmt.setString(1,order);
//4.執行sql語句(主要執行DQL、DML……)
//5.處理查詢結果集(只有當第4步執行的是select語句時,才有這第5步)
rs = stmt.executeQuery();
boolean flag = rs.next();
while (flag){
//列表標籤
String empno = rs.getString("empno");
String ename = rs.getString("ename");
String sal = rs.getString("sal");
System.out.println(empno+","+ename+","+sal);
flag = rs.next();
}
}catch (Exception e){
e.printStackTrace();
}finally { //為保證一定關閉,放在finally關閉
//6.釋放資源(使用完資源後一定要關閉資源,java和資料庫屬於程式間通訊,開啟後一定要關閉)
//按照從小到大的順序關閉
try{
if(stmt != null)
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
3.3.為什麼有些情況Statement可以,PreparedStatement不行????
- 以3.2為例:把emp表內的資料按工資升序、降序排列
- 正常sql語句應該是:
-
select empno ,ename,sal from emp order by sal desc;//降序
-
select empno ,ename,sal from emp order by sal asc; //升序
- Statement程式:
-
//4.執行sql語句(主要執行DQL、DML……) String sql = " select * from emp order by sal "+order; System.out.println(sql);
- PreparedStatement程式:
-
//3.獲取資料庫操作物件(專門執行sql語句的物件) String sql = " select * from emp order by sal ? "; //預編譯 stmt = conn.prepareStatement(sql); //給佔位符傳值 stmt.setString(1,order); System.out.println(sql);
4.PreparedStatement的CRUD
4.1.增insert:向表中插入資料——(no=14,name=Bob,class_id=2020)
-
//增 //insert into t_stu(no,name,class_id) values (14,'Bob',2020); String sql = "insert into t_stu(no,name, class_id ) values (?,?,?)"; ps = conn.prepareStatement(sql); // 給佔位符傳值 ps.setInt(1,14); ps.setString(2,"Bob"); ps.setInt(3,2020); ps.executeUpdate();
4.2.刪delete:刪除no>20的全部資料
-
//刪 delete from t_stu where no>20; String sql3 = "delete from t_stu where no>?"; ps = conn.prepareStatement(sql3); // 給佔位符傳值 ps.setInt(1,20); ps.executeUpdate();
4.3.改update:no=20的資料,name都改為Anna
-
// //改 update t_stu set name='Anna' where no = 20 ; String sql2 = "update t_stu set name= ? where no=?"; ps = conn.prepareStatement(sql2); // 給佔位符傳值 ps.setString(1,"Anna"); ps.setInt(2,20); ps.executeUpdate();
4.4.查retrieve:查詢no>10的所有資料
-
//查 select * from t_stu where no>10; String sql4 = " select * from t_stu where no>?"; ps = conn.prepareStatement(sql4); // 給佔位符傳值 ps.setInt(1,10); //4.執行sql語句(主要執行DQL、DML……) rs = ps.executeQuery(); //專門執行DQL查詢語句
/**
* 演示preparedStatement 的 CRUD
*/
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.*;
public class preparedStatement {
public static void main(String[] args) {
//JDBC程式碼
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.註冊驅動(作用:告訴java程式,即將要連線哪個品牌的資料庫)
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連線(表示JVM的程式和資料庫程式之間的 通道 開啟了,使用完之後 必須關閉)
String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址連結
String user = "root";
String password = "333";
conn = DriverManager.getConnection(url,user,password);
//3.獲取資料庫操作物件(專門執行sql語句的物件)
//一個問號表示一個佔位符
//增 insert into t_stu(no,name, class_id ) values (?,?,?)
String sql = "insert into t_stu(no,name, class_id ) values (?,?,?)";
//insert into t_stu(no,name,class_id) values (20,'James',2020);
ps = conn.prepareStatement(sql);
// 給佔位符傳值
ps.setInt(1,14);
ps.setString(2,"Bob");
ps.setInt(3,2020);
ps.executeUpdate();
// 刪 delete from t_stu where no=30;
String sql3 = "delete from t_stu where no>?";
ps = conn.prepareStatement(sql3);
// 給佔位符傳值
ps.setInt(1,20);
ps.executeUpdate();
// //改 update t_stu set name='dior' where no > ?;
String sql2 = "update t_stu set name= ? where no=25?";
ps = conn.prepareStatement(sql2);
// 給佔位符傳值
ps.setString(1,"Anna");
ps.setInt(2,20);
ps.executeUpdate();
//查
String sql4 = " select * from t_stu where no>?";
ps = conn.prepareStatement(sql4);
// 給佔位符傳值
ps.setInt(1,10);
//程式執行到此,會傳送sql語句”框子“給DBMS,然後它進行‘預編譯
//4.執行sql語句(主要執行DQL、DML……)
rs = ps.executeQuery(); //專門執行DQL查詢語句
//5.處理查詢結果集(只有當第4步執行的是select語句時,才有這第5步)
boolean flag = false;
flag = rs.next();
while (flag){
//列表標籤
String no = rs.getString("no");
String name = rs.getString("name");
String class_id = rs.getString("class_id");
System.out.println(no+","+name+","+class_id);
flag = rs.next();
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.釋放資源(使用完資源後一定要關閉資源,java和資料庫屬於程式間通訊,開啟後一定要關閉)
try{
if(ps != null)
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
相關文章
- 使用PreparedStatement實現CRUD操作
- Java中Statement與PreparedStatement與CallableStatement之間的區別 - javarevisitedJava
- SQL隱碼攻擊問題以及解決方法SQL
- SQL隱碼攻擊方法SQL
- XSS與SQL隱碼攻擊SQL
- SQL隱碼攻擊SQL
- SQL隱碼攻擊及如何解決SQL
- SQL隱碼攻擊(pikachu)SQL
- MYSQL SQL隱碼攻擊MySql
- 三種方法助您緩解SQL隱碼攻擊威脅SQL
- SQL隱碼攻擊原理是什麼?如何防範SQL隱碼攻擊?SQL
- WEB三大攻擊之—SQL隱碼攻擊與防護WebSQL
- 【網路安全】什麼是SQL隱碼攻擊漏洞?SQL隱碼攻擊的特點!SQL
- SQL隱碼攻擊導圖SQL
- SQL隱碼攻擊語句SQL
- pikachu-SQL隱碼攻擊SQL
- SQL隱碼攻擊總結SQL
- DVWA-SQL Injection(SQL隱碼攻擊)SQL
- 攻擊JavaWeb應用[4]-SQL隱碼攻擊[2]JavaWebSQL
- 攻擊JavaWeb應用[3]-SQL隱碼攻擊[1]JavaWebSQL
- CSRF 攻擊 及其 Laravel 解決Laravel
- SQL隱碼攻擊關聯分析SQL
- Mybatis如何防止SQL隱碼攻擊MyBatisSQL
- Nacos Derby SQL隱碼攻擊漏洞SQL
- MSSQL SQL隱碼攻擊 總結SQL
- Oracle SQL隱碼攻擊 總結OracleSQL
- SQL隱碼攻擊-堆疊注入SQL
- SQL隱碼攻擊基礎原理SQL
- 【網路安全入門】SQL隱碼攻擊是什麼?SQL隱碼攻擊危害有哪些?SQL
- 教程:使用PreparedStatement訪問DLA
- 如何防止SQL隱碼攻擊?網路安全防禦方法SQL
- 什麼是SQL隱碼攻擊(SQLi)SQL
- SQL隱碼攻擊讀寫檔案SQL
- Mura CMS processAsyncObject SQL隱碼攻擊漏洞ObjectSQL
- SQL隱碼攻擊速查表(上)SQL
- 預防SQL隱碼攻擊筆記SQL筆記
- SQL隱碼攻擊——時間盲注SQL
- SQL隱碼攻擊基礎入門SQL