昨天面试官问到一个mybatis是如何预防sql注入的问题,当时脑子直接就想到:预编译,然后就GG
只是大概的知道mybatis会将我们写好的sql语句进行预编译,然后将传参进去的参数都看作是字符串类型,在传参这里卡住不出现异常的sql注入;
讲完之后觉得太水了,后面细化学习一下:
mybatis中的#和$的区别:
1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号;可以预防SQL注入
如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username="111", 如果传入的值是id,则解析成的sql为where username="id".
2、$将传入的数据直接显示生成在sql中;不进行预编译,无法预防SQL注入,需要在代码中做处理
如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111;如果是传入 or 1=1;这样子筛选条件失效了;
如果传入的值是;drop table user;,则解析成的sql为: where username=;drop table user; 删除数据表,导致
3、#方式能够很大程度防止sql注入,$方式无法防止Sql注入。
4、$方式一般用于传入数据库对象,例如传入表名.
5、一般能用#的就别用$,若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
6、在MyBatis中,“${xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。
mybatis实现防止sql注入:
MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。如果我们想防止SQL注入,要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:
select id, username, password, role from user where username=? and password=?
不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。
【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。