讲这个思路之前我要先说一下SQL注入的原理
前端黑客发现注入点(username,password,cookie,token,agent,HTTP头信息等等),输入参数字符串(ASCII码流),后端接收字符串(ASCII码流),然后和自己代码里的select等字符串(在传给MySQL时也是ASCII码流)进行拼接,执行SQL语句,MySQL执行语句的时候无法区分哪些是程序员写的,哪些是黑客写的,所以这是问题根源。
还有一点,SQL数据库会自动解析0x开头的比如(0x2CA335434长度不限直到非0-F的字符对应的ASCII机器码出现),然后将对应的ASCII码流解析成字符串,此操作只进行一次,就算解析后的字符串长成这个样子 (admin' and 1=1 #),SQL 引擎也不会把他当成控制字符了,就是单纯的字符串,用汇编的说法就是这个数据在DS段,不在CS段了。
而PHP中恰巧bin2hex()就是干这个的,原理就是把用户输入的东西转成十六进制ASCII码,头部再拼接一个0x给MYSQL声明下这是用户的东西,不是后端程序员写的,这样后端和MYSQL通信时MYSQL就能区分哪些是指令哪些是数据,最关键的这个操作还不影响正常用户的提交,这个防御思路即使用sqlmap也无能为力。
但是上述思路有个bug,那就是假如注入点也就是后端程序员需要查询的SQL数据库的列属性是数字int型的话,上述思路尽管杜绝了SQL注入,但会导致正常业务,因此针对int型的列名查询,需要考虑Intval()过滤。
实现代码如下:
<?php
#已知当$a='sad'时; 里面就是是!@#%^&*('')<"">等特殊符号也无所谓
// var_dump(bin2hex($a)); // 结果是 string(6) "736164"
//因此
//当需要查询或写入数据库$_GET/POST['uername或password或cookie或token等']时,
function sqlwaf($xx,$type='str'){
if ($type === 'str'){
$xx = bin2hex($xx);
return '0x'.$xx;
}
if ($type === 'int'){
$xx = Intval($xx);
return $xx;
}
}
$a = sqlwaf($_GET['username'],$type='str'/'int');
# 之后再进行SQL语句的拼接即可,比如
$sql ='select * from user where username='.$a;
$sql ='select * from user where id='.$a;
?>