Day 3
2019年7月7日。
这是我学习数据库的第三天。
这一天,我学到了以下的知识。
SQL注入
简介
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。
原理
SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
根据相关技术原理,SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤,从而执行了非法的数据查询。基于此,SQL注入的产生原因通常表现在以下几方面:①不当的类型处理;②不安全的数据库配置;③不合理的查询集处理;④不当的错误处理;⑤转义字符处理不合适;⑥多个提交处理不当。
攻击
当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。相关的SQL注入可以通过测试工具pangolin进行。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。在某些表单中,用户输入的内容直接用来构造动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码,根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。
SQL注入在JDBC上的应用
我们发现,根据JDBC的相关代码,按照相应的方法进行操作,无论密码是否正确,都提示我们登陆成功,这显然是不 合理的。问题出在哪里呢?
字符串拼接后的SQL语句是:
select * from users where username = 'abc' or 1 = 1 and password = '"+password+"';
可以看到,运行到or的时候已经是条件成立,所以无论后面是否正确,无需验证密码即可登陆成功。
上面的问题是通过在SQL语句中添加特殊的字符,构成关键字,改变了程序运行轨迹,从而对数据进行操作。
那么如何防范这种问题呢?就需要用到接下来介绍的PreparedStatement对象。
PreparedStatement对象
优点
PreperedStatement是Statement的子类,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言,PreperedStatement可以避免SQL注入的问题
Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。并且PreperedStatement对于sql中的参数, 允许使用占位符的形式进行替换,简化sql语句的编写。
PreparedStatement对象的运用示例如下:
import java.sql.*;
import java.util.Scanner;
//防止SQL注入
public class Demo03 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1.获取数据库连接
connection = JDBCUtils.getConnection();
//prepareStatement解决sql注入问题
//select * from users where id = 1
//System.out.print("请输入你要查询的用户名:");
//String username = scanner.nextLine();
/*
preparedStatement【推荐使用】 和 Statement;
相同点 : 都是用来执行sql语句的
不同点 :
Statement 不安全 , 不能预编译SQL , 不能使用占位符 , 容易造成SQL语句拼接错误
preparedStatement 安全 , 预编译SQL语句 , 可以使用 ? 占位符 ,SQL更清晰。
如何给preparedStatement的占位符赋值
preparedStatement.setXXX [xxx:对应占位符数据的类型] (?索引[从1开始] , 传入的值)
Statement 先写SQL语句再执行; Statement.execute(SQL)
preparedStatement 直接编译SQL , 调用方法执行 ; preparedStatement.execute();
*/
preparedStatement = connection.prepareStatement("select * from users where id = ? and name = ?");
preparedStatement.setInt(1,4);
preparedStatement.setString(2,"qinjiang");
//preparedStatement.setString(1,username);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5.释放资源
JDBCUtils.closeAll(resultSet,preparedStatement,connection);
}
}
}