JDBC第二天:防sql攻擊

1 什麼是SQL攻擊

      在需要用戶輸入的地方,用戶輸入的是SQL語句的片段,最終用戶輸入的SQL片段與我們DAO中寫的SQL語句合成一個完整的SQL語句!例如用戶在登錄時輸入的用戶名和密碼都是爲SQL語句的片段!

2 演示SQL攻擊

首先我們需要創建一張用戶表,用來存儲用戶的信息。

CREATE TABLE user(

uid CHAR(32) PRIMARY KEY,

username VARCHAR(30) UNIQUE KEY NOT NULL,

PASSWORD VARCHAR(30)

);

 

INSERT INTO user VALUES('U_1001', 'zs', 'zs');

SELECT * FROM user;

現在用戶表中只有一行記錄,就是zs。

下面我們寫一個login()方法!

public void login(String username, String password) {

Connection con = null;

Statement stmt = null;

ResultSet rs = null;

try {

con = JdbcUtils.getConnection();

stmt = con.createStatement();

String sql = "SELECT * FROM user WHERE " +

"username='" + username +

"' and password='" + password + "'";

rs = stmt.executeQuery(sql);

if(rs.next()) {

System.out.println("歡迎" + rs.getString("username"));

} else {

System.out.println("用戶名或密碼錯誤!");

}

} catch (Exception e) {

throw new RuntimeException(e);

} finally {

JdbcUtils.close(con, stmt, rs);

}

}

 

下面是調用這個方法的代碼:

login("a' or 'a'='a", "a' or 'a'='a");

 

這行當前會使我們登錄成功!因爲是輸入的用戶名和密碼是SQL語句片段,最終與我們的login()方法中的SQL語句組合在一起!我們來看看組合在一起的SQL語句:

SELECT * FROM tab_user WHERE username='a' or 'a'='a' and password='a' or 'a'='a'

 

3 防止SQL攻擊

  1. 過濾用戶輸入的數據中是否包含非法字符;
  2. 分步交驗!先使用用戶名來查詢用戶,如果查找到了,再比較密碼;
  3. 使用PreparedStatement。

 

4 PreparedStatement是什麼?

PreparedStatement叫預編譯聲明!

PreparedStatement是Statement的子接口,你可以使用PreparedStatement來替換Statement。

PreparedStatement的好處:

  1. 防止SQL攻擊;
  2. 提高代碼的可讀性,以可維護性;
  3. 提高效率。

 

5 PreparedStatement的使用

  1. 使用Connection的prepareStatement(String sql):即創建它時就讓它與一條SQL模板綁定;
  2. 調用PreparedStatement的setXXX()系列方法爲問號設置值
  3. 調用executeUpdate()或executeQuery()方法,但要注意,調用沒有參數的方法;

 

String sql = “select * from tab_student where s_number=?”;

PreparedStatement pstmt = con.prepareStatement(sql);

pstmt.setString(1, “S_1001”);

ResultSet rs = pstmt.executeQuery();

rs.close();

pstmt.clearParameters();

pstmt.setString(1, “S_1002”);

rs = pstmt.executeQuery();

 

在使用Connection創建PreparedStatement對象時需要給出一個SQL模板,所謂SQL模板就是有“?”的SQL語句,其中“?”就是參數。

在得到PreparedStatement對象後,調用它的setXXX()方法爲“?”賦值,這樣就可以得到把模板變成一條完整的SQL語句,然後再調用PreparedStatement對象的executeQuery()方法獲取ResultSet對象。

注意PreparedStatement對象獨有的executeQuery()方法是沒有參數的,而Statement的executeQuery()是需要參數(SQL語句)的。因爲在創建PreparedStatement對象時已經讓它與一條SQL模板綁定在一起了,所以在調用它的executeQuery()和executeUpdate()方法時就不再需要參數了。

PreparedStatement最大的好處就是在於重複使用同一模板,給予其不同的參數來重複的使用它。這纔是真正提高效率的原因。

 

所以,建議大家在今後的開發中,無論什麼情況,都去需要PreparedStatement,而不是使用Statement。

package cn.itcast.demo3;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Test;

/**
 * PreapredStatement
 * 防SQL攻擊
 * @author cxf
 *
 */
public class Demo3 {
	/**
	 * 登錄
	 * 使用username和password去查詢數據
	 * 若查出結果集,說明正確!返回true
	 * 若查出不出結果,說明用戶名或密碼錯誤,返回false
	 * @param username
	 * @param password
	 * @return
	 * @throws Exception 
	 */
	public boolean login(String username, String password) throws Exception {
		/*
		 * 一、得到Connection
		 * 二、得到Statement
		 * 三、得到ResultSet
		 * 四、rs.next()返回的是什麼,我們就返回什麼
		 */
		// 準備四大參數
		String driverClassName = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/mydb3";
		String mysqlUsername = "root";
		String mysqlPassword = "123";
		// 加載驅動類
		Class.forName(driverClassName);
		// 得到Connection
		Connection con = DriverManager.getConnection(url, mysqlUsername, mysqlPassword);
		
		// 得到Statement
		Statement stmt = con.createStatement();
		
		// 給出sql語句,調用stmt的executeQuery(),得到ResultSet
		String sql = "select * from t_user where username='" + username + "' and password='" + password + "'";
System.out.println(sql);
		ResultSet rs = stmt.executeQuery(sql);
		
		return rs.next();
	}
	
	/**
	 * SQL攻擊!
	 * @throws Exception
	 */
	@Test
	public void fun1() throws Exception {
		//select * from t_user where username='a' or 'a'='a' and password='a' or 'a'='a'
		String username = "a' or 'a'='a";
		String password = "a' or 'a'='a";
		boolean bool = login(username, password);
		System.out.println(bool);
	}
	
	public boolean login2(String username, String password) throws Exception {
		/*
		 * 一、得到Connection
		 * 二、得到Statement
		 * 三、得到ResultSet
		 * 四、rs.next()返回的是什麼,我們就返回什麼
		 */
		// 準備四大參數
		String driverClassName = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/mydb3?useServerPrepStmts=true&cachePrepStmts=true";
		String mysqlUsername = "root";
		String mysqlPassword = "123";
		// 加載驅動類
		Class.forName(driverClassName);
		// 得到Connection
		Connection con = DriverManager.getConnection(url, mysqlUsername, mysqlPassword);
		
		///////////////////////////////////////
		///////////////////////////////////////
		
		
		/*
		 * 一、得到PreparedStatement
		 * 1. 給出sql模板:所有的參數使用?來替代
		 * 2. 調用Connection方法,得到PreparedStatement
		 */
		String sql = "select * from t_user where username=? and password=?";
		PreparedStatement pstmt = con.prepareStatement(sql);
		
		/*
		 * 二、爲參數賦值
		 */
		pstmt.setString(1, username);//給第1個問號賦值,值爲username
		pstmt.setString(2, password);//給第2個問號賦值,值爲password
		
		ResultSet rs = pstmt.executeQuery();//調用查詢方法,向數據庫發送查詢語句
		
		pstmt.setString(1, "liSi");
		pstmt.setString(2, "123");
		
		pstmt.executeQuery();
		
		
		return rs.next();
	}
	
	@Test
	public void fun2() throws Exception {
		//select * from t_user where username='a' or 'a'='a' and password='a' or 'a'='a'
		String username = "zhangSan";
		String password = "123";
		boolean bool = login2(username, password);
		System.out.println(bool);
	}
	
	/**
	 * 測試JdbcUtils.getConnection()
	 * @throws SQLException 
	 * @throws ClassNotFoundException 
	 * @throws IOException 
	 */
	@Test
	public void fun3() throws SQLException {
		Connection con = JdbcUtils.getConnection();
		System.out.println(con);
		Connection con1 = JdbcUtils.getConnection();
		System.out.println(con1);
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章