1. 程式人生 > >JDBC及防sql注入攻擊

JDBC及防sql注入攻擊

哪有什麼一夜成名,都是百鍊成鋼

JDBC

JDBC(Java DataBase Connectivity,java資料庫連線)又SUN公司開發,是一種用於執行SQL語句的Java API,可以為多種關係資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成。JDBC提供了一種基準,據此可以構建更高階的工具和介面,使資料庫開發人員能夠編寫資料庫應用程式。
簡單地說,JDBC 可做三件事:與資料庫建立連線、傳送 操作資料庫的語句並處理結果。
程式碼示例:

Connection con = DriverManager.getConnection("jdbc:odbc:wombat","login",
"password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}

對以上程式碼分析

Connection 連線 代表了java和資料之間的通道,橋樑
Statement  語句 可以用來執行 insert, update, delete , select ...
ResultSet  結果集 代表的是查詢的結果

類
DriverManager 工具類,獲取連線物件
SQLException  異常物件 是 Exception的子型別,屬於檢查異常

操作步驟順序

  1. 載入驅動 (Driver) jdbc的驅動就是一個連線工廠,生成的產品是連線物件
    com.mysql.jdbc.Driver 是Driver的mysql實現類
    oracle.jdbc.Driver 是Driver的oracle實現類
   Class.forName("驅動類名");
   例如:
   Class.forName("com.mysql.jdbc.Driver");

jdbc 3.0 以上版本都可以省略載入驅動這一步

  1. 獲取連線物件
   DriverManager.getConnection(url, 使用者名稱, 密碼); // 內部呼叫了  Driver 物件獲取資料庫連線

url 的格式 :

jdbc:mysql://ip地址:埠號/資料庫名?引數

例如:mysql資料庫

Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost:3306/test", "root", "root");
  1. 建立語句
Statement stmt = conn.createStatement();
  1. 執行sql
int rows = stmt.executeUpdate(); // 用來執行 insert , update, delete , DDL , 返回值代表影響行數
// delete from student where sid = 1008;  返回影響行數是0;

ResultSet rs = stmt.executeQuery(); // 用來執行 select
boolean moreRows = rs.next() 取得下一條記錄, 
moreRows 是true ,表示有下一條記錄, false 表示沒有了
while(rs.next()) {

}
// 迭代器模式
  1. 釋放資源 ( 先開啟的資源後關閉 )
rs.close();
stmt.close();
conn.close();

JDBC及sql注入攻擊

1.建立一個user表,用來登入比對。

create table user(username varchar(50) not null,password varchar(50));

2.新增user資訊。

insert into user(username,password) values ('張三','123');

3.通過jdbc連線sql資料庫具體程式碼示例。
首先測試一下jdbc的連線,通過sql語句

select * from user where username='張三' and password='123';

來查詢出張三的賬號密碼
程式碼如下(通過字串拼接實現select傳參)

public class TestJdbc {
    public static void main(String[] args) throws SQLException {
   
        getSelect("張三","123");//使用者名稱 張三 密碼 123
        return;
    }
    private static void getSelect(String name, String psd) throws SQLException {
        //1.建立連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        //2.準備sql語句
        Statement statement = conn.createStatement();
        //3.執行sql語句並返回一個集合
        ResultSet resultSet = statement.executeQuery("select * from user where username='"+name+"' and  password='"+psd+"'");
        //4.如果有下一條資料返回true,沒有返回false
        while (resultSet.next()){
            //resultSet本身值在在第一條記錄的前面.
            System.out.println(resultSet.getString("username")+resultSet.getString("password"));
        }
        statement.close();
        conn.close();
    }
}

輸出結果
張三123

上面我們傳的引數是‘張三’,‘123’ 都是正確的賬號密碼
那麼,接下來我們將引數換一下密碼看能否到正確的資訊
要替換的引數如下

getSelect("張三","'or '1'='1");

將密碼換成“'or ‘1’='1”。

public class TestJdbc {
    public static void main(String[] args) throws SQLException {

        getSelect("張三","'or '1'='1");//使用者,密碼
        return;
    }
    private static void getSelect(String name, String psd) throws SQLException {
        //1.建立連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        //2.準備sql語句
        Statement statement = conn.createStatement();
        //3.執行sql語句並返回一個集合
        ResultSet resultSet = statement.executeQuery("select * from user where username='"+name+"' and  password='"+psd+"'");
        //4.如果有下一條資料返回true,沒有返回false
        while (resultSet.next()){
            //resultSet本身值在在第一條記錄的前面.
            System.out.println(resultSet.getString("username")+resultSet.getString("password"));
        }
        statement.close();
        conn.close();
    }
}

輸出結果
張三123

咦,到這裡是不是有點奇怪,明明密碼是123 隨便寫了個“'or ‘1’='1”也行,其實我們將我們的sql語句拿出來分析一下,答案顯而易見。

select * from user where username='"+name+"' and  password='"+psd+"

這是我們的sql語句,我們將第一次的引數 “張三” “'or ‘1’='1”帶入得到新的sql.

select * from user where username='張三' and  password=''or '1'='1'

看到這裡是不是發現了問題的真正所在,我們輸入的值直接將本來的sql語句都給發生了改變。
那麼,如何防治這種情況出現呢??
1.對引數記憶體做檢查,內部不能有sql關鍵字例如:or
2.用 PreparedStatement代替Statement

PreparedStatement 預編譯語句物件

Statement stmt = conn.createStatement();
stmt.executeUpdate(String sql);

  1. 需要預先提供sql語句
PreparedStatement psmt = conn.prepareStatement(String sql);
  1. 可以在sql中用?佔位某個值
insert into username(username,password) values(?, ?)
  1. 給?賦值
使用PreparedStatement中一系列以 set開頭的方法
setString(?的位置,)
setInt(?的位置,)
setDate(?的位置,)

psmt.setString(1, "張三");
psmt.setString(2, "123");
  1. 執行sql
psmt.executeUpdate();

注意: ?能夠佔位的只有值, 不能是表名、列名、關鍵字

5.用PreparedStatement的程式碼示例

public class TestJdbc {
    public static void main(String[] args) throws SQLException {

        getSelect("張三","123");//使用者,密碼
        return;
    }
    private static void getSelect(String name, String psd) throws SQLException {
        //1.建立連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        //2.準備sql語句,用? 站位
        String sql ="select * from user where username=? and password=?";
        PreparedStatement statement = conn.prepareStatement(sql);
        設定?的值,用順序去定位
        statement.setString(1,name);
        statement.setString(2,psd);
        //3.執行sql語句並返回一個集合
        ResultSet resultSet = statement.executeQuery();
        //4.如果有下一條資料返回true,沒有返回false
        while (resultSet.next()){
            //resultSet本身值在在第一條記錄的前面.
            System.out.println(resultSet.getString("username")
            +resultSet.getString("password"));
        }
        statement.close();
        conn.close();
    }
}