SQL注入及防止SQL注入
•SQL注入
SQL注入是通过操作输入来修改事先定义好的SQL语句,对用户输入的字符串进行过滤,转义,限制或处理不严谨,导致用户可以通过输入精心构造的字符串去非法获取到数据库中的数据,以达到执行代码对服务器进行攻击的方法。
现有一个数据库test中的表user,可以通过账号name,密码pass登录,查看id
登录代码
package JDBCtest; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Scanner; /* * 用户登录 */ public class Demo4 { public static void main(String[] args) throws Exception { // 1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.创建连接 String url = "jdbc:mysql:///test"; String username = "root"; String password = "1234"; Connection connection = DriverManager.getConnection(url, username, password); // 接收用户名密码 Scanner sc = new Scanner(System.in); String name = sc.next(); String pass = sc.next();// 3.sql语句 String sql = "Select * from user where name='" + name + "' and pass ='" + pass + "'"; // System.out.println(sql); // 4.获取sql对象statement Statement statement = connection.createStatement(); // 5.执行sql语句 ResultSet rs = statement.executeQuery(sql); // 6.登录 if (rs.next()) { System.out.println("登录成功"); } else { System.out.println("登录失败"); } // 7.释放资源 statement.close(); connection.close(); } }
通过表中账号密码登录成功
由于账号或密码错误登录失败
以上可以正确登录成功或失败
注意!
如果此时我这样输入 【lihua 'or'1'='1】,也成功登录了,但是数据库没根本没有这条数据
这是为什么呢?让我们从代码里找问题!
String sql = "Select * from user where name='" + name + "' and pass ='" + pass + "'";
为了调用数据库,使用字符串拼接SQL语句
让我们打印一下输入 【lihua 'or'1'='1】的sql语句一探究竟
select语句中的where条件可以看做两组并列(注意and在前先执行,or后执行)
由于'1'='1'为TRUE,所以以上语句等价于肯定会登录成功!
同理输入【'or'1'='1'# xxx】也能登陆成功
这是由于#在SQL中是注释符号,以上语句等价于,于是就和上述情况一样了。
•防止SQL注入
使用PrepareStatement代替Statement
在代替时 有两点注意
1:sql语句发生变化,不使用单纯字符串拼接,使用占位符"?"代替(利用转义,从根本上解决问题)
2:"?"处使用setXxx填坑
3:执行sql语句时,executeQuery()无参数!(由于PrepareStatement继承与Statement,若添加参数则使用的还是Statement的executeQuery()方法)
package JDBCtest; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Scanner; public class Demo5 { public static void main(String[] args) throws Exception { // 1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.创建连接 String url = "jdbc:mysql:///test"; String username = "root"; String password = "1234"; Connection connection = DriverManager.getConnection(url, username, password); // 3.定义sql语句 Scanner sc = new Scanner(System.in); String name = sc.next(); String pass = sc.next(); String sql = "Select * from user where name=? and pass=?";//改变 // 4.获取sql对象PrepareStatement PreparedStatement prepareStatement = connection.prepareStatement(sql);//改变 prepareStatement.setString(1, name);//改变 prepareStatement.setString(2, pass);//改变 // 5.执行sql语句 ResultSet rs = prepareStatement.executeQuery();//改变 // 6.处理结果 if (rs.next()) { System.out.println("登陆成功"); } else { System.out.println("登录失败"); } // 7.释放资源 prepareStatement.close();//改变 connection.close(); } }
再运行一下看结果
查看一下mysql日志文件
prepareStatement利用转义,解决了字符串拼接问题
注意 prepareStatement()有对sql语句预编译的功能,可以节省时间,但默认是关闭的,要在url里使用useServerPrepStmts=true开启
String url = "jdbc:mysql:///test?useServerPrepStmts=true";
若不开启的话
开启后