SQL注入的原理,网上文章较多,就不展开了。简言之,将用户提交过来的参数和SQL关键字一起拼接出来的SQL语句是不安全的,如果采用拼接且服务器侧没有对提交过来的参数进行合法性验证或过滤,导致原有SQL语句的语义被改变,或改变查询条件,或追加语句执行恶意操作等等。
下面说说SQL注入的防御措施:
最佳实践,不要拼接SQL语句,采取预编译(变量绑定)措施,使用占位符问号?代替实际参数来创建SQL语句,如果已经采取了此措施,且没有将列名作为参数的情况,可不再需要其它控制措施了。
以Java为例,,使用PreparedStatement预编译之后,它就不允许改变SQL语句的逻辑结构。
String sql= "select * from articles where id=?" ; PreparedStatement psql=conn.preparedStatement(sql); psql.setString(1,myname); Result rs= psql.executeQuery();
在.Net中,使用SqlParameter将可变部分参数化;在PHP+MySQL的组合中,使用mysqli->prepare进行预编译。
但是,预编译(参数化)并没有考虑列名作为参数的场景,如order by ColumnName,这时还需要检查列名是否在白名单当中。
不过,还需要强调一点,如果仍旧采取拼接SQL语句的做法,即使部分采取了预编译的作法,仍可能存在漏洞。
第二种较为简洁的控制措施,按照参数的类型分为两种:整数型(参数为整数)、字符型(语句中为参数前后添加单引号),服务器侧对整型的参数进行验证是否为整型,对字符型参数,将一个单引号转换为两个单引号;再考虑列名作为参数的场景检查列名是否在白名单当中。
如SQL语句 select * from articles where id=123 中的参数id视为整数型,
select * from articles where id=’123’ 中的id则为字符型
如果不区分整型和字符型,统一采取将一个单引号转换为两个单引号的作法,则对整型参数没有起到保护作用,因为针对整型的注入攻击是可以不用单引号的。
第三种,则是不区分整型或字符型,统一在服务器侧对参数验证/过滤(至少过滤 ‘ “ ; - / () 等符号),其中单引号、双引号主要被攻击者用于语句配对,分号用于断句并增加新的语句,减号和斜杠一般用于注释。不过,这种方式难免挂一漏万,注入方式变种很多,不提倡采取此方式。