标签归档:弱类型

PHP弱类型安全问题汇总

1、== 与 ===

PHP中的两种比较符号:

$a==$b

$a===$b

区别在于$a==$b会转换类型后比较,而$a === $b会比较变量类型。

如果一个数值和一个字符串比较,那么会将字符串转换为数值。

<?php

var_dump('a' == 0); //true

var_dump('a123' == 0); //true

var_dump('123a' == 123); //true

var_dump('1' == 1);//true

var_dump(NULL == 0);//true

var_dump(NULL == false);//true

var_dump(NULL == '');//true

var_dump(0 === 'abcdefg');//false

?>

 

2、Magic Hash

在进行比较运算时,如果遇到了0e\d+这种字符串,就会将这种字符串解析为科学计数法,其最终都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是0e\d+这种字符串,那么PHP将会认为他们相同,都是0。

案例:

<?php

       if (isset($_GET['v1']) && isset($_GET['v2'])) {

           $logined = true;

        $v1 = $_GET['v1'];

        $v2 = $_GET['v2'];

        if (!ctype_alpha($v1)) {$logined = false;}

        if (!is_numeric($v2) ) {$logined = false;}

        if (md5($v1) != md5($v2)) {$logined = false;}



        if ($logined){

            echo "login success";

        } else {

            echo "login failed";

        }

    }

?>

一个是纯数字型,一个只能出现字符,最终需要两个md5相等。

md5('240610708')

//0e462097431906509019562988736854.

md5('QNKCDZO')

//0e830400451993494058024219903391

实际环境中概率非常低。0e\d+字符串如下。

QNKCDZO

0e830400451993494058024219903391

240610708

0e462097431906509019562988736854

s878926199a

0e545993274517709034328855841020

s155964671a

0e342768416822451524974117254469

s214587387a

0e848240448830537924465865611904

s214587387a

0e848240448830537924465865611904

s878926199a

0e545993274517709034328855841020

s1091221200a

0e940624217856561557816327384675

s1885207154a

0e509367213418206700842008763514

s1502113478a

0e861580163291561247404381396064

s1885207154a

0e509367213418206700842008763514

s1836677006a

0e481036490867661113260034900752

s155964671a

0e342768416822451524974117254469

s1184209335a

0e072485820392773389523109082030

s1665632922a

0e731198061491163073197128363787

s1502113478a

0e861580163291561247404381396064

s1836677006a

0e481036490867661113260034900752

s1091221200a

0e940624217856561557816327384675

s155964671a

0e342768416822451524974117254469

s1502113478a

0e861580163291561247404381396064

s155964671a

0e342768416822451524974117254469

s1665632922a

0e731198061491163073197128363787

s155964671a

0e342768416822451524974117254469

s1091221200a

0e940624217856561557816327384675

s1836677006a

0e481036490867661113260034900752

s1885207154a

0e509367213418206700842008763514

s532378020a

0e220463095855511507588041205815

s878926199a

0e545993274517709034328855841020

s1091221200a

0e940624217856561557816327384675

s214587387a

0e848240448830537924465865611904

s1502113478a

0e861580163291561247404381396064

s1091221200a

0e940624217856561557816327384675

s1665632922a

0e731198061491163073197128363787

s1885207154a

0e509367213418206700842008763514

s1836677006a

0e481036490867661113260034900752

s1665632922a

0e731198061491163073197128363787

s878926199a

0e545993274517709034328855841020

使用===来比较HASH数值来避免该问题

 

3、switch()

switch是数字类型的case的判断时,switch会将其中的参数转换为int类型

<?php

       $i ="1xxx";

       switch ($i) {

       case 0:

       case 1:

           echo $i;

       }

?>

 

4、in_array()

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。

<?php

$xx = array(0,1,2,'3');

var_dump(in_array('abc', $xx));  //true

var_dump(in_array('1bc', $xx));      //true

var_dump(in_array('abc', $xx, TRUE));  //false

var_dump(in_array('1bc', $xx, TRUE));  //false

?>

可以看到比较时’abc’会转换为0,’1bc’会转换为1再进行比较。

 

5、strcmp()

int strcmp ( string $str1 , string $str2 )

参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

Freebuf的文章写到在5.3之前的php中,如果传入的比较数据非字符串类型,显示了报错的警告信息后,将return 0。

实际测试这个结论不太准确,但是的确与PHP版本有关

CentOS5.5 + PHP 5.1.6:

返回1,测试程序:

<?php

    $password="1234";

    var_dump(strcmp(array(1,2,3,'x'), $password)); //输出1

?>

Windows + PHP 5.3.29

返回NULL,测试程序:

<?php

    $password="1234";

    var_dump(strcmp(array(1,2,3,'x'), $password)); //输出NULL

?>

修改程序

<?php

       $password="1234";

       if (strcmp($_GET['password'], $password) == 0) {

              echo "Success";

       }

?>

访问http://127.0.0.1/x.php?password[]=xx

 

6、is_numeric()

bool is_numeric ( mixed $var )

如果变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。

而如果该值是0x十六进制格式,同样会返回TRUE,就可能存在二次注入问题。

代码如下:

<?php

$name = $_GET['name'];

$con = mysql_connect("localhost","root","hehe123");

if (!$con)

{

die('Could not connect: ' . mysql_error());

}



mysql_select_db("test", $con);

if (is_numeric($name)) {

mysql_query("insert into users values (3," . $name . ",'test')");

}

?>

1′ union select 1,2,3的十六进制为0x312720756e696f6e2073656c65637420312c322c33

访问:http://127.0.0.1/x.php?name=0x312720756e696f6e2073656c65637420312c322c33

mysql> select * from users;

+----+-----------------------+----------+

| id | username              | password |

+----+-----------------------+----------+

|  3 | 1' union select 1,2,3 | test     |

+----+-----------------------+----------+

1 row in set (0.00 sec)

 

参考文章

http://www.phpchina.com/portal.php?mod=view&aid=40031

http://www.freebuf.com/articles/web/129607.html

http://www.freebuf.com/news/67007.html

http://blog.topsec.com.cn/ad_lab/php代码审计之弱类型引发的灾难/?utm_source=tuicool&utm_medium=referral