背景
本文详细讨论sql注入的那些事情。聊聊自己的看法。并且会提供sql注入的例子。
sql注入的例子
例子就不用jdbc的 API 来演示了。我直接用了springboot+mybatis来模拟出来
1、代码
-
写一个controller的接口
@GetMapping("/user/list") public Object listUser( @RequestParam String name ) { return userService.listUser(name); }
-
service层的代码
@Override public List<UserDTO> listUser(String name) { return userMapper.listUser(name); }
-
dao层(这是最关键的)
@Select({"<script>", "select * from user where name='${name}'", "</script>"}) List<UserDTO> listUser(@Param("name") String name);
可以看到使用了 ${xxx} 而不是 #{xxx}
2、测试和总结
上述web跑起来后,参数传入
-
正常访问:
http://localhost:8888/user/list?name=stone
-
sql注入:
http://localhost:8888/user/list?name=stone' or '1'='1
这种方式,最终构造出来的sql是
select * from user where name='stone' or '1'='1'
,利用了or以及1=1恒成立。
3、进一步补充
除了上述,其实还可以利用注释符号,比如有些用 #
作为注释,有些用 --
,mysql可以使用 #。如下:
select * from user_login where name='${name}' and password='${password}'
name传入 ' or 1=1; #
,password随便传什么值,最终会变成
select * from user_login where name='' or 1=1; #' and password='随便传什么值'
#
之后是注释,会被忽略。另外甚至可以传入 or 1=1;drop database test ;#
之类的,或者 or 1=1;delete from xxx ;#
,不过后面2个mybatis会报错,我没试验成功,总之你认为sql注入非常危险就是了。
解决方法
- 如果你使用mybatis,使用 #{xxx} 替代 ${xxx} 就好了
- 如果你用jdbc,使用PreparareStatement即可,即避免使用Statement,并且避免sql语句字符串拼接参数
PS:貌似mybatis-plus框架还搞了一些拦截器,拦截有问题的入参,有必要搞得这么复杂吗?不是使用 #{xxx} 就能解决吗? 难道就算使用 #{xxx} 也还是存在sql注入风险??
总结
其实感觉sql注入也没那么可怕。
首先,如果是内部使用的系统或者toB的话,人员没那么杂。
其次,就算是C端用户,也要猜到哪些操作对应后台的sql是可以进行注入的,就算知道了这条sql语句有问题,也很难知道sql的全貌,构造有问题的入参的时候很可能会导致sql根本就不能执行,即大概率sql执行是出错的。
最后,只要后端人员稍微规范一点用 #{xxx} 就可避免了。