1. 程式人生 > >SQL攻擊與PreparedStatement

SQL攻擊與PreparedStatement

1.什麼是SQL攻擊。

    在需要使用者輸入的地方,使用者輸入的是SQL語句的片段,終端使用者輸入的SQL片段與我們DAO中寫的SQL語句合成一個完整的SQL語句!例如使用者在輸入使用者名稱以及登陸密碼都是SQL語句時,我們的程式就會將其嵌入到我們的SQL語句中,最終可能出現數據庫的錯誤。

2.PreparedStatement

1)它是Statement介面的子介面;

2)強大之處

    2.1)防SQL攻擊。

    2.2)提高程式碼的可讀性。

    2.3) 提高效率


例項:

public boolean login(String username,String password) throws Exception{
		ResultSet re = null;
		Connection con = null;
		Statement stmt = null;
		String sql = "select * from users where Id = '"
					+username+"' and Password = '"+password+"'";
		boolean result = false;
		try{
			//設定四大引數
			String driverClassName = "com.mysql.jdbc.Driver";
			String url = "jdbc:mysql://localhost:3306/timebookstore";
			String sqlUserName = "***";
			String sqlPassword = "123456";
			//載入類
			Class.forName(driverClassName);
			//連線資料庫獲取Connection物件
			con = DriverManager
					.getConnection(url,sqlUserName,sqlPassword);
			//獲取Statement,用於向資料庫傳送SQL語句
			stmt = con.createStatement();
			//傳送查詢語句
			re=stmt.executeQuery(sql);
			result = re.next();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(re != null)re.close();
			if(stmt != null)stmt.close();
			if(con != null)con.close();
		}
		return result;
	}
	
	@Test
	public void fun2() throws Exception{
		String username = "abc";
		String password = "123";
		System.out.println(login(username,password));
	}

控制檯輸出的結果是false,表示驗證不成功。但是如果使用者名稱和密碼改為如下:

@Test
	public void fun2() throws Exception{
		String username = "abc' or 'a' = 'a";
		String password = "123' or 'a' = 'a";
		System.out.println(login(username,password));
	}

也即完整的sql語句為:

select * from users where Id = 'abc' or 'a' = 'a' and Password = '123' or 'a' = 'a'

很明顯這一句查詢語句返回表中的所有內容,所以結果是true。這就是sql攻擊.


下面的例子使用PreparedStatement防止sql攻擊。

public boolean login(String username,String password) throws Exception{
		ResultSet re = null;
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = "select * from users where Id = ? and password = ?";
		boolean result = false;
		try{
			//設定四大引數
			String driverClassName = "com.mysql.jdbc.Driver";
			String url = "jdbc:mysql://localhost:3306/timebookstore";
			String sqlUserName = "***";
			String sqlPassword = "123456";
			//載入類
			Class.forName(driverClassName);
			//連線資料庫獲取Connection物件
			con = DriverManager
					.getConnection(url,sqlUserName,sqlPassword);
			//獲取PreparedStatement,用於向資料庫傳送SQL語句
			pstmt = con.prepareStatement(sql);
			//設定sql的引數
			pstmt.setString(1, username);
			pstmt.setString(2,password);
			//執行sql語句
			re=pstmt.executeQuery();
			result = re.next();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(re != null)re.close();
			if(pstmt != null)pstmt.close();
			if(con != null)con.close();
		}
		return result;
	}
	
	@Test
	public void fun2() throws Exception{
		String username = "abc' or 'a' = 'a";
		String password = "123' or 'a' = 'a";
		System.out.println(login(username,password));
	}

使用PreparedStatement的結果是false,與我們預期的一樣了。

由上面的例子可以看出使用PreparedStatement,編寫的sql語句更加直觀,還可以防sql攻擊。

String sql = "select * from users where Id = '"+username+"' and password = '"+password+"'";//Statement
String sql = "select * from users where Id = ? and password = ?";//PreparedStatement

?號表示引數,下面有設定該引數的程式碼。

pstmt = con.prepareStatement(sql);
			//設定sql的引數
			pstmt.setString(1, username);
			pstmt.setString(2,password);