什么是Sql注入?
Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击.
比如:
String sql = "select name from users where user_id = '"+str+"'";
我们用str来接受用户传递的id,再与原来sql语句拼接执行.但如果有人输入特殊的字符串,如:
1' union select user,password from users#,拼接后的sql语句就变成了:
select name from users where user_id = '1' union select user,password from users#'
通过sql注入实现了对数据库数据的获取.
再比如:
在我们实现登录界面时,用了这个语句:
String sql = "select * from users where username = '"+username+"' and password = '"+password+"'";
当查询到数据表中存在同时满足 username 和 password 字段时,会返回登录成功。
但如果在用户名中输入 123' or 1=1 #, 密码同样输入 123' or 1=1 # ,sql语句就变成了:
select * from users where username = '123' or 1=1 #' and password = '123' or 1=1 #';
实际执行的sql语句为:
select * from users where username = '123' or 1=1
实现了绕过验证
那么,我们该如何防御sql注入呢?
限制数据类型
有当get到的id为数字或者数字字符时才能执行下一步.
缺点:
正则表达式匹配传入参数
对传入语句进行匹配,在匹配到特定字符(如Union select)时停止运行.
缺点:正则表达式消耗性能,因此攻击时可以构造大量的正常语句‘骗’过服务器,当后台对数据的处理达到最大限制时就会放弃匹配后面构造的非法语句,从而略过这个数据包.
将特殊字符转义
例如:
str.trim().replaceAll("\\s", "").replace("\\", "\\\\\\\\")
.replace("_", "\\_").replace("\'", "\\'")
.replace("%", "\\%").replace("*", "\\*");
使用预编译语句
使用预编译相当于是将数据于代码分离的方式,把传入的参数绑定为一个变量,用?表示,攻击者无法改变SQL的结构
比如jdbc中的PreparedStatement
string sql = "select * from people p where p.id = ? and p.name = ?";
preparedstatement ps = connection.preparestatement(sql);
ps.setint(1,id);
ps.setstring(2,name);
ps.executequery();
预编译可以将后来传递的变量当作纯字符串使用,不会被当做之前的SQL语句被带入数据库执行,避免了一些非法操作.