9.1.5 漏洞防范

9.1.5 漏洞防范

SQL注入漏洞虽然是目前最泛滥的漏洞,不过要解决SQL注入漏洞其实还比较简单。在PHP中可以利用魔术引号来解决,不过魔术引号在PHP 5.4后被取消,并且gpc在遇到int型的注入时也会显得不那么有效了,所以通常用得多的还是过滤函数和类,像discuz、dedecms、phpcms等程序里面都使用过滤类,不过如果单纯地过滤函数写得不够严谨,也会出现绕过的情况,像这三套程序就都存在绕过问题。当然最好的解决方案还是利用预编译的方式,下面就来看看这三种方式的使用方法。

1.gpc/rutime魔术引号

通常数据污染有两种方式,一种是应用被动接收参数,类似于GET、POST等;还有一种是主动获取参数,类似于读取远程页面或者文件内容等。所以防止SQL注入的方法就是要守住这两条路。magic_quotes_gpc负责对GET、POST、COOKIE的值进行过滤,magic_quotes_runtime对从数据库或者文件中获取的数据进行过滤。通常在开启这两个选项之后能防住部分SQL注入漏洞被利用。为什么说是部分,因为它们只对单引号(')、双引号(")、反斜杠(\)及空字符NULL进行过滤,在int型的注入上是没有多大作用的。

2.过滤函数和类

过滤函数和类有两种使用场景,一种是程序入口统一过滤,像框架程序用这种方式比较多,另外一种是在程序进行SQL语句运行之前使用,除了PHP内置的一些过滤单引号等函数外,还有一些开源类过滤union、select等关键字。

(1)addslashes函数

addslashes函数过滤的值范围和GPC是一样的,即单引号(')、双引号(")、反斜杠(\)及空字符NULL,它只是一个简单的检查参数的函数,大多数程序使用它是在程序的入口,进行判断如果没有开启GPC,则使用它对$_POST/$_GET等变量进行过滤,不过它的参数必须是string类型,所以曾经某些程序使用这种方式对输入进行过滤时出现了绕过,比如只遍历$_GET的值,当时并没有考虑到$_GET的值也是一个数组。我们来看一个例子如下:

上面的例子输出:phpsafe\'。

(2)mysql_[real_]escape_string函数

mysql_escape_string和mysql_real_escape_string函数都是对字符串进行过滤,在PHP 4.3.0以上版本才存在,如下字符受影响[\x00][\n][\r][\][']["][\x1a],两个函数唯一不一样的地方在于mysql_real_escape_string接受的是一个连接句柄并根据当前字符集转义字符串,所以推荐使用mysql_real_escape_string。

使用举例:

当请求该文件?id=1’时,上面代码输出:select*from test where id='1\''

(3) intval等字符转换

上面我们提到的过滤方式,在int类型注入时效果并不好,比如可以通过报错或者盲注等方式来绕过,这时候intval等函数就起作用了,intval的作用是将变量转换成int类型,这里举例intval是要表达一种方式,一种利用参数类型白名单的方式来防止漏洞,对应的还有很多如floatval等。

应用举例如下:

以上代码输出:1。

3.PDO prepare预编译

如果之前了解过.NET的SqlParameter或者java里面的prepareStatement,那么就很容易能够理解PHP pdo的prepare,它们三个的作用是一样的,都是通过预编译的方式来处理数据库查询。

我们先来看一段代码:

上面这段代码虽然使用了pdo的prepare方式来处理sql查询,但是当PHP版本<5.3.6之前还是存在宽字节SQL注入漏洞,原因在于这样的查询方式使用了PHP本地模拟prepare,再把完整的SQL语句发送给MySQL服务器,并且使用了set names'gbk'语句,所以由于PHP和MySQL编码不一致的原因导致SQL注入,正确的写法应该是使用ATTR_EMULATE_PREPARES来禁用PHP本地模拟prepare,代码如下: