前言
SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库,本文参考网上资料整理。
如何防御SQL注入
「外部数据不可信任」的原则,纵观Web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到的,就是从变量的检测、过滤、验证下手,确保变量是开发者所预想的。
检查变量数据类型和格式:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
过滤特殊符号:对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。
绑定变量,使用预编译语句:MySQL的mysqli驱动提供了预编译语句的支持。实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构。
为什么预编译(PrepareStatement)可以防止sql注入
原理是采用了预编译的方法,先将SQL语句中可被客户端控制的参数集进行编译,生成对应的临时变量集,再使用对应的设置方法,为临时变量集里面的元素进行赋值,赋值函数setString()
,会对传入的参数进行强制类型检查和安全检查,所以就避免了SQL注入的产生。下面具体分析。
Statement之所以会被sql注入是因为SQL语句结构发生了变化。比如:
SQL = “SELECT * FROM users WHERE (name = ‘” + userName + “’) and (pw = ‘”+ passWord +”’);”
//如果恶意填入
userName = “1’ OR ‘1’=’1”;
passWord = “1’ OR ‘1’=’1”;
//将变成
SQL = SELECT * FROM users WHERE (name = ‘1’ OR ‘1’=’1’) and (pw = ‘1’ OR ‘1’=’1’);
//相当于
SQL = SELECT * FROM users;
而Preparement样式为
SELECT * FROM users WHERE userName=? and passWord=?
该SQL语句会在得到用户的输入之前先用数据库进行预编译,这样的话不管用户输入什么用户名和密码的判断始终都是并的逻辑关系,防止了SQL注入。
简单总结,参数化能防注入的原因在于,语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑。
Mybatis 中的 $ 和 # 使用方法的区别
#{}
将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #{id}
,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”。${}
将传入的数据直接显示生成在sql中。如:order by ${id}
,如果传入的值是111,那么解析成sql时的值为order by 111, 如果传入的值是id,则解析成的sql为order by id。
故#
方式能够很大程度防止sql注入,而$
方式无法防止Sql注入。$
方式一般用于传入数据库对象,例如传入表名。
MyBatis 排序时使用 order by 动态参数时需要注意,用
$
而不是#
,因为用#
就成了字符串而不是关键字。【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。
mybatis是如何做到防止sql注入的
【底层实现原理】在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。