SQL注入,是利用web程序的请求,构建特殊的输入参数,将恶意的SQL语句注入到后台数据库引擎中,最终可以欺骗服务器执行恶意SQL。
车牌识别SQL注入一般我们在根据用户的输入参数进行查询数据库时,需要对参数进行预编译;如果是不能预编译的场景,那么需要对输入参数采取类型检查、转义映射、安全过滤等措施。
一、什么是SQL预编译
很多时候,我们可能会反复地执行一条SQL语句,每次执行的时候只不过是参数值不同而已,SQL语句的结构并未发生变化。
如果没有预编译,那么每次在执行这些SQL的时候,数据库都需要进行词法分析、语义分析、制定执行计划、执行并返回结果这些操作,比较耗时和耗费资源。因此,数据库有一种机制可以将这些结构相同的SQL进行预编译,形成固定的执行计划,下次再执行的时候,直接拿这些执行计划,套入传入的参数执行即可,大大提高了效率。
SQL语句解析过程如何启用数据库的预编译功能呢?我们使用JDBC的时候,使用PreparedStatement就可以达到目标。如此,SQL的语句会提前发送给数据库进行预编译,变量值就使用占位符来表示,等到真的要执行SQL的时候,将参数填入占位符按照事先的执行计划执行即可。
二、为什么SQL预编译可以防止SQL注入
PreparedStatement和Statement最大的区别在于,前者会提前将SQL发送给数据库进行预编译,提前确定了执行计划,对于输入的参数,无论是什么,都会按照原先的计划进行填入执行,不会再改变SQL的逻辑结构;而后者则会随着输入参数的不同改变SQL逻辑结构,因此有注入的风险。
三、MyBatis是如何做到防止SQL注入的
-
#{}
的传值方式,本质上就是使用JDBC的PreparedStatement,无论参数是什么,都只是被当做参数执行,不会改变预编译好的SQL结构和执行计划; -
${}
的传值方式,则是使用JDBC的Statement,参数的值会改变SQL的逻辑结构。
一般${}
主要用在传入数据库对象的场景中,比如需要使用动态的表名和列名时,这种场景下无法使用#{}
,我们就需要手动对参数进行过滤,来确保SQL的安全;
四、不能使用预编译时该如何方式SQL注入
如下是一个对输入参数进行检查的例子:
protected static boolean sqlValidate(String str) {
//统一转为小写
str = str.toLowerCase();
String badStr = "'|select|update|and|or|delete|insert|truncate|char|into|iframe|href|script|activex|html|flash"
+ "|substr|declare|exec|master|drop|execute|"
+ "union|;|--|+|,|like|%|#|*|<|>|$|@|\"|http|cr|lf|<|>|(|)";//过滤掉的sql关键字,可以手动添加
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
return false;
}
}
return true;
}
五、参考文献
如下这篇文章有更详细的介绍:
SQL注入详解
网友评论