0. SQL注入简介
SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库。
注入攻击的总体思路
- 寻找到SQL注入的位置
- 判断服务器类型和后台数据库类型
- 针对不通的服务器和数据库特点进行SQL注入攻击
1. SQL注入原理
以登录账号为例,要求我们输入账号(userName)和密码(passWord),输入完点击确定后台将用Java使其生成一条sql语句。
正常情况
用Java原声的产生sql语句方式,而不是用 PreparedStatement,代码是这样写的:
String sql = "select * from table where user_name='" + userName +"' and pass_word='" + passWord + "'";
将会生成一条语句(这里加入我输入账号:Lucas 密码:12356)
select * from table where user_name ='Lucas' and 'pass_word=12356';
异常情况
用户输入不是Lucas而是
’ or 1=1 –
产生的sql语句是这样的
select * from table where user_name ='' or 1=1 -- 'and pass_word='123'
注:这样前面的user_name判断条件无效。 并且加入– 注释后面的所有语句!攻击者堂皇的进入系统。 可能此人更心怀不轨,不是要进入系统而是破坏。所以他可能会写:
’;DROP DATABASE (DB NAME) —
这样翻译的sql语句就是:
select * from table where user_name='';DROP DATABASE (DB NAME) -- 'and pass_word='123'
其后果可想而知......
2. 防止手段
PreparedStatement
说明:采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。
String sql= "select * from users where username=? and password=?; PreparedStatement preState = conn.prepareStatement(sql); preState.setString(1, userName); preState.setString(2, password); ResultSet rs = preState.executeQuery();
原理
sql注入只对sql语句的准备(编译)过程有破坏作用,而PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,而不再对sql语句进行解析,准备,因此也就避免了sql注入问题。
使用正则吧表达式过滤传入参数
需引入包:
import java.util.regex.*;
采用正则表达式将包含有 单引号('),分号(;) 和 注释符号(--)的语句给替换掉来防止SQL注入
public static String TransactSQLInjection(String str) { return str.replaceAll(".*([';]+|(--)+).*", " "); // 我认为 应该是return str.replaceAll("([';])+|(--)+",""); } userName=TransactSQLInjection(userName); password=TransactSQLInjection(password); String sql="select * from users where username='"+userName+"' and password='"+password+"' " Statement sta = conn.createStatement(); ResultSet rs = sta.executeQuery(sql);
如下是具体的正则表达式:
检测SQL meta-characters的正则表达式 :/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix
修正检测SQL meta-characters的正则表达式 :/((\%3D)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(:))/i
典型的SQL 注入攻击的正则表达式 :/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix
检测SQL注入,UNION查询关键字的正则表达式 :/((\%27)|(\’))union/ix(\%27)|(\’)
检测MS SQL Server SQL注入攻击的正则表达式:/exec(\s|\+)+(s|x)p\w+/ix
总结:
- 一切输入都是不安全的:对于接口的调用参数,要进行格式匹配,例如admin的通过name查询的接口,与之匹配的Path应该使用正则匹配(因为用户名中不应该存在特殊字符),从而确保传入参数是程序控制范围之内的参数,即只接受已知的良好输入值,拒绝不良输入。
注意:验证参数应将它与输出编码技术结合使用。
- 利用分层设计来避免危险:前端尽量静态化,尽量少的暴露可以访问到DAO层的接口到公网环境中,如果现有项目,很难修改存在注入的代码,可以考虑在web服务之前增加WAF进行流量过滤,当然代码上就不给hacker留有攻击的漏洞才最好的方案。也可以在拥有nginx的架构下,采用OpenRestry做流量过滤,将一些特殊字符进行转义处理。
- 尽量使用预编译SQL语句:由于动态SQL语句是引发SQL注入的根源。应使用预编译语句来组装SQL查询。
- 规范化:将输入安装规定编码解码后再进行输入参数过滤和输出编码处理;拒绝一切非规范格式的编码。