前言
在前段时间项目拿去做了一个入网安全测试,结果测出来例如SQL盲注
,跨站点脚本攻击(XSS)
,跨站点伪造(CSRF)
等一系列问题.
今天先对解决SQL盲注
问题做一个小的总结.
问题
SQL盲注
,相信大家都不会怎么陌生,如果没听说过得话,那么肯定听说过SQL注入
.
SQL盲注
是一种Web 系统的安全漏洞,属于比较严重的那种.SQL注入
有很多种方式,
而SQL盲注
就是SQL注入
的其中一种方式.
在安全级别中,SQL盲注
是一种威胁程度很高的安全漏洞.
用SQL查询语句去猜解表名、字段、数据等信息,使用外部输入来构造SQL 命令的全部或一部分,
如果在用户输入中没有对SQL语法充分地除去或引用,那么生成的SQL可绕过安全性检查,
或者插入其他用于修改后端数据库的语句,或者执行系统命令。
错误的SQL也会被调用,并返回请求响应,攻击者利用这些漏洞进行系统攻击。
注入实例:
//请求地址
String getUrl="xxx.jsp?user=admin&password=1' or '1'='1 ";
//接收参数
String user=request.getParameter("user");
String password=request.getParameter("password");
//拼装sql
String checkSql="select count(*) from user where username='"+user+"' and password ='"+password+"'";
获取参数合成后的sql变成了
select count(*) from users where userid='admin' and passwd='1' or '1'='1'
这条SQL将会返回所有的记录数。
示例代码:
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取链接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo","root","admin");
//模拟账号
String user="zhouq";
//模拟密码
String password="1' or '1'='1";
//拼接 statement 需要执行的 sql
StringBuilder sqlByStatement=new StringBuilder("select count(*) from user where ");
sqlByStatement.append(" username='").append(user).append("'");
sqlByStatement.append(" and password='").append(password).append("'");
//准备 PreparedStatement 执行的预编译 sql
String sqlByPrepareStatement="select count(*) from user where username=? and password=?";
Statement statement = conn.createStatement();
ResultSet query = statement.executeQuery(sqlByStatement.toString());
System.out.println("账户:"+ user);
System.out.println("密码:"+ password);
System.out.println("===========华丽的分割线===========");
if(query.next()){
long count = query.getLong(1);
System.out.println("Statement 查询结果:" + count);
}
PreparedStatement ps = conn.prepareStatement(sqlByPrepareStatement);
//设置第一个参数
ps.setString(1, user);
//设置第二个参数
ps.setString(2, password);
ResultSet resultSet = ps.executeQuery();
if(resultSet.next()){
long count = resultSet.getLong(1);
System.out.println("PrepareStatement 查询结果: " + count);
}
//资源关闭 操作...
//
输出结果:
账户:zhouq
密码:1' or '1'='1
===========华丽的分割线===========
Statement 查询结果:1
PrepareStatement 查询结果: 0
解决方案
SQL注入攻击
是利用设计上的漏洞,在目标服务器上运行SQL语句进行攻击,动态生成SQL语句时没有对用户输入的数据进行验证是SQL注入攻击得逞的主要原因.
对于JDBC而言,SQL注入
攻击只对 Statement
有效,对PreparedStatement
是无效的,这是因为PreparedStatement
不允许在插入时改变查询的逻辑结构.
绕过验证,但这种手段只对Statement
有效,对PreparedStatement
无效.
尽量使用
PreparedStatement
执行sql .
例如上面的 SQL 查询语句.
在 Statement 中 的sql 语句:select count(*) from user where username='admin' and passwd='1' or '1'='1'
.
然后 statement.executeQuery(sql) 执行 ,
在PreparedStatement
中 的sql 语句:select count(*) from user where username=? and password=?
,然后再对对应的 ? 赋值.
Statement
是将整个sql 语句作为一个字符串链接到一起执行.
PreparedStatement
中则 将1' or '1'='1
做为一个字符串 赋值给第二个 ? ,作为 “密码” 字段对应的值.这样一来 ,sql 注入就不存在了.使用
ORM
框架. 如 Mybatis
在Mybatis
框架中.#{xx}
,使用的是PreparedStatement
,会有类型转换.也是比较安全的.
select count(*) from user where username=#{username} and password=#{password}
最终编译的sql为:
select count(*) from user where username=? and password=?
结语:
以上的这些,只是对最终sql 的处理.如果想彻底的防止SQL盲注
的系统.
那么我们还需要对用户发过来的请求进行过滤,其中请求中的参数值很有可能包括一些危险的字符,
这些字符就是我们需要去过滤处理的问题了.关于处理危险字符这些问题,又会涉及到 其他的漏洞 比如 XSS
,CSRF
等等.