一、什么是sql注入
public class TestSql { public static void main(String[] args) { Scanner inScanner = new Scanner(System.in); System.out.println("请输入用户名"); String username = inScanner.nextLine(); System.out.println("请输入密码"); String password = inScanner.nextLine(); String sql = "select * from user where username = '"+username+"' and password = '"+password+"'"; search(sql); } public static void search(String sql) { try { Class.forName("com.mysql.jdbc.Driver"); // 1.加载驱动 //2.建立连接 Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/login?useUnicode=true&characterEncoding=utf-8", "root", ""); //3.创建执行的SQL语句 Statement statement = (Statement) connection.createStatement(); //4.执行sql语句 ResultSet re = (ResultSet) statement.executeQuery(sql); //5.处理结果 if(re.next()) { System.out.println("查询成功..."); }else { System.out.println("查询失败..."); } //6.释放资源 if(re!=null) { re.close(); } if(statement !=null) { statement.close(); } if (connection !=null) { connection.close(); } } catch (Exception e) { // TODO Auto-generated catch block System.out.println("找不到驱动类,加载失败"); e.printStackTrace(); } } }
以上的程序我们经过测试没什么问题,但是当输入如下数据的时候,一个可怕的问题出现了。
数据库中只有一个数据
那么为什么会产生这个现象呢?
我们将拼接好的sql语句拿出来
select * from user where username =' 111' and password = '1' or '1'='1';
我们将SQL语句拿到Navicat运行一下
这种通过传参就能改变SQL语句原本规则的操作就是SQL注入,这个在实际开发中当然是危险的,攻击者可以把SQL命令插入到Web表单的输入域或页面请求的查询字符串中,欺骗服务器执行恶意的SQL命令。
二、为什么会产生sql注入问题呢?
我们可以把sql语句的执行流程大致分为一下几个步骤:
1.本地sql语句拼接
2.发送sql语句给DBMS
3.DBMS进行sql编译
造成sql注入的原因在于我们在本地拼接了一条“有安全隐患的”sql语句。之后我们将拼接好的sql语句发送给DBMS,DBMS将“有安全隐患”的sql语句进行了编译执行。
这里的重点是:用户的信息参与到了编译过程,而这个信息出现了问题
三、如何解决sql注入问题呢?
其实解决方法很简单:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
要想让用户的信息不参与SQL语句的编译,那么就需要预先对sql语句的框架进行编译,然后再给给sql赋值。预编译完成之后我们的DBMS只需要执行我们的sql语句,没必要再次编译。可以使用java.sql.PreparedStatement接口完成预编译,PreparedStatement是属于预编译的数据库操作对象
使用PreparedStatement需要注意的几点
①:占位符
String sql = "select * from user where username = ? and password = ?";
我们拼写好的sql语句不在使用直接拼接赋值的方式,而是采用 ? 占位符进行代替
②:创建方式
statement.setString(1, usernanme); statement.setString(2, password);
总体代码如下
public class TestSql { public static void main(String[] args) { Scanner inScanner = new Scanner(System.in); System.out.println("请输入用户名"); String username = inScanner.nextLine(); System.out.println("请输入密码"); String password = inScanner.nextLine(); String sql = "select * from user where username = ? and password = ?"; search(sql,username,password); } public static void search(String sql,String usernanme,String password) { try { Class.forName("com.mysql.jdbc.Driver"); // 1.加载驱动 //2.建立连接 Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/login?useUnicode=true&characterEncoding=utf-8", "root", "2020"); //3.创建执行的SQL语句 PreparedStatement statement = (PreparedStatement) connection.prepareStatement(sql); // 给占位符赋值 statement.setString(1, usernanme); statement.setString(2, password); //4.执行sql语句 ResultSet re = (ResultSet) statement.executeQuery(); //5.处理结果 if(re.next()) { System.out.println("查询成功..."); }else { System.out.println("查询失败..."); } //6.释放资源 if(re!=null) { re.close(); } if(statement !=null) { statement.close(); } if (connection !=null) { connection.close(); } } catch (Exception e) { // TODO Auto-generated catch block System.out.println("找不到驱动类,加载失败"); e.printStackTrace(); } } }