浅谈PHP弱类型安全

小飞 · 2015/01/04 10:25php

0x00 弱类型初探


没有人质疑php的简单强大,它提供了不少特性供开发者使用,其中一个就是弱类型机制。mysql

在弱类型机制下 你可以执行这样的操做程序员

#!php
<?php
$var = 1;
$var = array();
$var = "string";
?>
复制代码

php不会严格检验传入的变量类型,也能够将变量自由的转换类型。sql

好比 在$a == $b的比较中数组

  • $a = null; $b = false; //为真
  • $a = ''; $b = 0; //一样为真

然而,php内核的开发者本来是想让程序员借由这种不须要声明的体系,更加高效的开发,因此在几乎全部内置函数以及基本结构中使用了不少松散的比较和转换,防止程序中的变量由于程序员的不规范而频繁的报错,然而这却带来了安全问题。安全

0x02 知识预备 php内核之zval结构


在PHP中声明的变量,在ZE中都是用结构体zval来保存的函数

zval的定义在zend/zend.hui

#!c
typedef struct _zval_struct zval;  

struct _zval_struct {  
    /* Variable information */  
    zvalue_value value;     /* value */  
    zend_uint refcount__gc;  
    zend_uchar type;    /* active type */  
    zend_uchar is_ref__gc;  
};  

typedef union _zvalue_value {  
    long lval;  /* long value */  
    double dval;    /* double value */  
    struct {  
        char *val;  
        int len;  
    } str;  
    HashTable *ht;  /* hash table value */  
    zend_object_value obj;  
} zvalue_value;
复制代码

其中php经过type判断变量类型 存入valuespa

如上也就是php内核中弱类型的封装,也是咱们后面讲的全部东西的原理和基础。code

0x03变量的强制转换


经过刚刚的了解,咱们知道zval.type决定了存储到zval.value的类型。

当源代码进行一些未限制类型的比较,或数学运算的时候,可能会致使zval.type的改变,同时影响zval.value的内容改变。

当int赶上string

cp.1 数学运算

当php进行一些数学计算的时候

#!php
var_dump(0 == '0'); // true
var_dump(0 == 'abcdefg'); // true  
var_dump(0 === 'abcdefg'); // false
var_dump(1 == '1abcdef'); // true 
复制代码

当有一个对比参数是整数的时候,会把另一个参数强制转换为整数。

至关于对字符串部分

intval再和整数部分比较,其实也就是改变了zval.type的内容 尤其注意的是,'1assd'的转换后的值是1,而‘asdaf’是0

也说明了intval会从第一位不是数字的单位开始进行

全部也有

#!php
var_dump(intval('3389a'));//输出3389
复制代码

这个例子就告诉咱们,永远不要相信下面的代码

#!php
if($a>1000){
    mysql_query('update ... .... set value=$a')
}
复制代码

你觉得这时候进入该支的万无一失为整数了

其实$a多是1001/**/union...

cp.2 语句条件的松散判断

举个例子
php的switch使用了松散比较. $which会被自动intval变成0
若是每一个case里面没有break ,就会一直执行到包含,最终执行到咱们须要的函数,这里是成功包含

#!php
<?php
if (isset($_GET['which']))
{
        $which = $_GET['which'];
        switch ($which)
        {
        case 0:
        case 1:
        case 2:
                require_once $which.'.php';
                break;
        default:
                echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
                break;
        }
复制代码

cp.3 函数的松散判断

#!php
var_dump(in_array("abc", $array));
复制代码

in_array — 检查数组中是否存在某个值 参数

needle 待搜索的值。

Note: 若是 needle 是字符串,则比较是区分大小写的。 haystack 这个数组。

strict 若是第三个参数 strict 的值为 TRUE 则 in_array() 函数还会检查 needle 的类型是否和 haystack 中的相同。

能够看到,只有加了strict才会对类型进行严格比较, 那么咱们再次把整形和字符串进行比较呢?

#!php
var_dump(in_array("abc", $array1));</br>
var_dump(in_array("1bc", $array2));
复制代码

它遍历了array的每一个值,而且做"=="比较(“当设置了strict 用===”)

结果很明显了

若是array1里面有个值为0,那么第一条返回就会为真//intval('abc')=0

若是array2里面有个值为1,那么第二条就会为真//intval('1bc')=1

array_search也是同样的原理

这里的应用就很普遍了,

不少程序员都会检查数组的值,

那么咱们彻底能够用构造好的int 0或1 骗过检测函数,使它返回为真

总结一下,在全部php认为是int的地方输入string,都会被强制转换,好比

#!php
$a = 'asdfgh';//字符串类型的a</br>
echo $a[2];  //根据php的offset 会输出'd'</br>
echo $a[x];  //根据php的预测,这里应该是int型,那么输入string,就会被intval成为0 也就是输出'a'
复制代码

当数组赶上string

这一个例子我是在德国的一个ctf中遇到,颇有意思
前面咱们讲的都是string和int的比较

那么array碰上int或者是string会有什么化学反应?

由php手册咱们知道

Array转换整型int/浮点型float会返回元素个数;

转换bool返回Array中是否有元素;转换成string返回'Array',并抛出warning。

那么实际应用是怎样的呢?

#!php
if(!strcmp($c[1],$d) && $c[1]!==$d){
...
}
复制代码

能够发现,这个分支经过strcmp函数比较要求二者相等且“==”要求二者不相等才能进入。

strcmp() 函数比较两个字符串。

该函数返回:

0 - 若是两个字符串相等
<0 - 若是 string1 小于 string2
>0 - 若是 string1 大于 string2
复制代码

这里的strcmp函数其实是将两个变量转换成ascii 而后作数学减法,返回一个int的差值。

也就是说键入'a'和'a'进行比较获得的结果就是0

那么若是让$array和‘a’比较呢?

#!php
http://localhost:8888/1.php?a[]=1
var_dump(strcmp($_GET[a],'a'));
复制代码

这时候php返回了null!

也就是说,咱们让这个函数出错从而使它恒真,绕过函数的检查。
下面给出一张松散比较的表格


0x04时时防备弱类型


做为一个程序员,弱类型确实给程序员书写代码带来了很大的便利,可是也让程序员忘记了
$array =array();的习惯。
都说一切输入都是有害的

那么其实能够说一切输入的类型也是可疑的,永远不要相信弱类型的php下任何比较函数,任何数学运算。不然,你绝对是被php出卖的那一个。

相关文章
相关标签/搜索