SQL注入

来自CloudWiki
跳转至: 导航搜索

SQL注入的概念

在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们数据库访问代码中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!  

创建示例表

登录验证方法

UserDaoSQL:

/**
	   * 验证用户名和密码是否正确
	   * @param usename,用户名
	   * @param password,密码
	   * @return 建立的数据库连接
	   */
	 public   boolean verify(String username, String password) {
		 boolean success=false;
		 try { 
			 
	    		Statement stmt = null;  
	    		ResultSet rs = null;       
	    		stmt = conn.createStatement();
	    		String sql = "SELECT * FROM user WHERE " + "name='" + username + 
	                   "' and password='" + password + "'";
	    		rs = stmt.executeQuery(sql);
	    		if(rs.next()) { 
	    			success=true;;
	    		} else {  
	    			success=false;
	    		}
	    	 }	catch (SQLException e) {
	             	System.out.println(e.getMessage());
	         }
	    	return success;   
	    }
   

下面是调用这个方法的代码:

package main;

import dao.UserDao;
import dao.UserDaoSQL;
import entity.User;

public class TestUser {
	public static void main(String[] args) throws Exception{
		 
		 UserDaoSQL uds =new UserDaoSQL();
		 
		 boolean success;
	         success=uds.verify("a' or 'a'='a", "a' or 'a'='a");
	         if(success) {
	    	     System.out.println("登录成功!");
	         }else {
	    	      System.out.println("登录失败!");
	         }
	    
	     
	 }
}

调用此方法会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:

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

预防SQL注入

预防SQL注入的方法:

  • 过滤用户输入的数据中是否包含非法字符;
  • 分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;
  • 使用PreparedStatement。

PreparedStatement

PreparedStatement也叫预编译声明。

PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。

PreparedStatement的好处:

  • 防止SQL攻击;
  • 提高代码的可读性,以可维护性;
  • 提高效率。

PreparedStatement的使用:

  • 使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;
String sql = "select * from user where name=? and password=?";
PreparedStatement pstmt = (PreparedStatement) con.prepareStatement(sql);
  • 调用PreparedStatement的setXXX()系列方法为sql命令设置值;
pstmt.setString(1, username);
pstmt.setString(2, password);  
  • 调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;
rs = pstmt.executeQuery();//执行查询    		

例如:

 
	/**
	   * 验证用户名和密码是否正确
	   * @param usename,用户名
	   * @param password,密码
	   * @return 建立的数据库连接
	   */
	 public   boolean verify(String username, String password) {
		 boolean success=false;
		
		 try { 
		    		
		    		ResultSet rs = null;   //建立一个空的结果集

		                //1.创建PreparedStatement 对象,让它与一条SQL模板绑定;
		    		String sql = "select * from user where name=? and password=?";
		    		PreparedStatement pstmt = (PreparedStatement) conn.prepareStatement(sql);
		    		

		                //使用2. setXXX()系列方法为sql命令设置值
		                pstmt.setString(1, username);//
		                pstmt.setString(2, password);  //setInt,setFloat

		                //3.执行查询 
		                rs = pstmt.executeQuery();//   		
		    		
		    		if(rs.next()) {  
		    			success=true;
		    		} else {  
		    			success=false;
		    		}
		    		pstmt.close();
		           
		}catch (SQLException e) {
		            System.out.println(e.getMessage());
		}
		
		return success;	    	
		    
	 }

在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。

PreparedStatement对象独有的executeQuery()方法是没有参数的,而Statement的executeQuery()是需要参数(SQL语句)的。因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了,所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。

知识点提炼

  1. PreparedStatement接口继承Statement, PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象。
  2. 作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。三种方法execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数
  3. 在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都尽量不要使用Statement.

参考文档: [1] https://blog.csdn.net/u011024652/article/details/51753481

[1] https://blog.csdn.net/ff445566/article/details/53705359

返回 Java程序设计