SQL防注入:使用base64构建安全查询
转载请注明:土人的国度 » SQL防注入:使用base64构建安全查询
SQL注入,主要是通过加入特殊字符,使用特殊编码将非法SQL语句注入到查询中,并得到返回数据
看到了一些防SQL注入的手段,自己也经过思考总结了一下,得到了一个流程:
1.查询前强制转换输入
对于提交的输入:
特定类型int, date等:进行强制类型转换,如intval, date_create;
字符串一律使用mysql_real_escape_string过滤
有格式的使用正则表达式进行验证
对于不需要模糊查询的字段,建议使用base64编码后再进一步处理并存入数据库,下文详述
2.使用预定义查询,不使用动态查询
预定义查询可以通过重用参数和执行计划来破坏SQL注入。不过它的深层次原理我还是没太明白。。。以后有空再研究下。。。关键问题是,参数在进行查询时是不是参与了SQL语义的识别,参数是否二进制安全。
3.查询后关闭错误输出
关闭错误输出,即使注入成功也不输出错误信息,让注入得不到结果。
4.针对不同查询,使用不同权限的SQL用户
最小化权限,使查询只能针对指定的表,指定的操作(select, update, insert, delete, alter)。防止注入后SQL代码获取权限之外的数据。这一点估计很少有注意的,甚至很多系统都用dbo甚至管理员权限的账户来进行数据库操作。
5.针对关键操作添加触发器记录日志
对于关键表关键字段的操作,添加触发器,并在日志中加以记录,做到被SQL注入后也可以追踪。亡羊补牢,为时未晚。
6.使用SQL语义安全的编码处理字符串
做到了上面五步,在整个查询过程中,就像加了5道牢固的锁,已经非常安全了。估计全世界没有几个人还能用SQL注入方式入侵这样的数据库拿到数据。不过,还是有SQL注入风险存在的,以php+mysql为例讲,主要问题出在经过mysql_real_escape_string过滤的字符串上。因为输入经过过滤,并不能100%保证过滤后的安全性,虽然出问题的概率很小,但在根本上还是有风险的。而对于经过intval或者date强制转换的字段,则在语义上保证了安全性。使用强制转换的数据再进行查询,可以完全避免SQL注入问题。
针对这个问题,我想到了一个使用base64编码不需要模糊查询字段再进行处理的方法。
例如登录时验证用户名密码这个注入的重灾区,这样处理:
$username = base64_encode($_POST[‘username’]);
$password = md5($_POST[‘password’] + $salt);
?>
接下来通过SQL查询验证的步骤完全一样。
那么为什么通过base64编码方式来避免SQL注入?
因为base64编码使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。不管你输入任何内容,包括注入非法的字符,都会被转化成上述这些对SQL语义绝对不会产生任何影响的安全字符。
看一个例子,如果在username里输入”or 1=1″会变成什么?结果是”b3IgMT0x”。这样在查询用户名时,不管输入了任何非法字符,经过base64都会变成对SQL语义不产生影响的安全字符,再经过查询或者进一步处理就十分安全了。而密码经过了加盐和md5处理,也会产生安全字符,所以查询起来也是安全的。
那我们可以不可以把所有内容都使用base64编码呢?答案是否定的。因为base64编码后的内容难以进行模糊查询,这跟base64使用4字节表示3字节内容导致的字节无法对齐有关。所以,base64可以用来处理长度不是很长的不需要模糊查询的字符串字段。例如用户名。为什么建议存储长度不长的内容呢?因为使用base64存储,占空间一般也会增长40%左右。
那么有没有一劳永逸既从根本上避免字符串的SQL注入(使用安全字符)又能进行模糊查询的方法呢?
我想了好久发现只有使用16进制编码最合适了,因为能让字符串每个字节对齐的最高效的产生安全编码的方式,只有使用a-e和0-9进行16进制编码了,不过代价高昂,需要多使用一倍的存储空间。
根据utf8编码的定义,如果使用汉字字符和全角标点,每个字的编码长度正好是3字节。所以,针对只有全角字符的字符串,就可以用base64编码来达到既可以防止SQL注入又能模糊查询的目的了,而且空间开销增加也不是很大。因为每个字都会转换成对应的4字节base64代码,查询时每4字节对齐查询即可。
防止SQL注入和处理很多其他安全问题一样,最根本的一条,永远不要相信从用户端提交的数据是正确的,永远都要验证用户提交的数据。最根本的验证不是模式匹配或者过滤,而是强制类型转换。做好这一点,网站安全性就进了一大步。