- 注入发生的原因
当我们使用字符串拼接来传SQL语句而没有使用预编译,用户就可以通过在表单中填写带有SQL关键字的数据来让数据库执行SQL语句。
SQL 数据库的操作是通过 SQL 语句来执行的,而无论是执行代 码还是数据项都必须写在 SQL 语句之中,这就导致如果我们在输入数据中加入了某些 字符,这些关键字就很可能在数据库写入或读取数据 时得到执行。
注入实例:
用户名:admin
密码:00000
String sql=“select * from users
where username=’”+username+"’ and password=’"+password+"’";
输出的SQL语句在MSQL中为:select * from users where username=‘admin’ and password=‘000000’
这样看是没有问题的,但是,当我们输入的
username为:"’ or 1=1 or ‘"时:
在MySQL中:select * from users where username=’"’ or 1=1 or ‘"’ and password=‘000000’
在MySQL中我们可以看到,只通过改变用户名,就能达到改变SQL语句语义的效果,这就是注入。
如何防止注入
使用预编译
PreparedStatement对象:
-
防止注入
继承Statement对象,在Statement对象中的sql语句需要拼接字符串传入,而拼接字符串会有sql
注入的风险。
使用Statement子对象PreparedStatement解决sql的注入问题。 -
性能:
Statement性能低,因为每次都需要对sql语句重新编译。拼接添加不同的数据的时候,sql语句在发生变化,所以每次只需都需要重新编译。
PreparedStatement对象:将sql语句中的数据和sql语句进行了分离,值的变化不需要重新编译sql语句,sql语句值需要编译一次,提高程序运行的效率。解决了sql的注入问题。 -
用法:
定义sql语句,将值和sql分离,sql中只有只的占位符
String sql=“select * from users where username=? and password=?”;
Statement statement = connection.createStatement();
PreparedStatement pstat=connection.prepareStatement(sql);
给预编译的sql语句中的占位符赋值
参数1:占位符的位置
参数2:站位对应的值
pstat.setString(1, username);
pstat.setString(2, password);
发送执行sql,获得ResultSet返回值对象,该对象封装了查询语句的虚拟表
结果集,resultSet对象相当于一张二维表
ResultSet result = statement.executeQuery(sql);
ResultSet result = pstat.executeQuery();