PHP弱类型

作者:yzc    发布于:

PHP弱类型

0x01 PHP弱类型的介绍

弱类型的语言对变量的数据类型没有限制,你可以在任何地时候将变量赋值给任意的其他类型的变量,同时变量也可以转换成任意地其他类型的数据。

类型转换是无法避免的,当不同类型的变量进行比较,或者接受参数,又或者两个变量类型不匹配的情况下,PHP都会自动进行类型转换,但正是由于PHP这种弱类型语言的特性,导致在类型转换的时候会出现各种各样的为题。

0x02 弱类型中的类型转换

1.字符串转换为数值时的数值类型问题:

当一个字符串被认定为数值时,如果字符串没有包含“.”,“e”,“E”时,而且被转换成的数值的值再整数范围内,则字符串转换为数值时的数值类型为int类型,其余的情况下为float类型

2.字符串类型和数值类型进行运算和比较时(+、-、*、/、==),字符串类型会被转换成数值类型进行比较(类型转换的问题),例:

1
2
3
4
5
6
7
<?php
$zzh=1 + "10.5"; // $zzh=11.5(float)
$zzh=1+"-1.3e3"; //$zzh=-1299(float)
$zzh=1+"bob-1.3e3";//$zzh=1(int)
$zzh=1+"2admin";//$zzh=3(int)
$zzh=1+"admin2";//$zzh=1(int)
?>

3.字符串类型转换成数值类型的原则:

(1)普通字符串的数值由其开始部分决定,如果字符串开始部分是合法的数值,则字符串的数值为其开始部分的值,否则为0。(也可以理解为从字符串的头开始寻找第一个不是数字的字符,该字符前面的数值即该字符串的数值,如果没有找到即开头就是字符,则该字符串的数值为0),例:

"1yzc" == 1 //true
"yzc" == 0  //true
"0yzc" == 0 //true
"yzc1" == 1 //false
"yzc1" == 0 //false

(2)0e\d+类型的字符串(例:0e123456)会被解析为科学计数法,在比较时,这样的字符串都是相等的,因为被认定为科学计数法进行比较,0无论进行了多少次方结果还是0,所以0e\d+``字符串比较时都是相等的,例

"0e1234" == "0e5678" //true
"0e1234" == "0eabc"  //false
"0e1234" == "0" //true
"0e1234" == 0   //true

(3)0x(十六进制的转换)开头的字符串会被解析成十进制再进行比较,所以0x开头的字符串与其十进制的字符串或者数值都是相等的,例:

"0x1e240" == "123456" //true
"0x1e240" == 123456   //true
"0x1e240" == "1x1e240"//false

4.一些其他的类型转换:

1
2
3
$yzc=null;
$zzh=false;
$yzc == $zzh; //true
1
2
3
$yzc='';
$zzh=null;
$yzc == $zzh; //true

0x03 内置函数的参数松散性

1.定义:在调用函数时,给函数传递一个函数无法接受的参数类型

2.出现参数松散性的函数:

(1)md5()函数:

PHP手册中的md5()函数的描述是string md5 ( string $str [, bool $raw_output = false ] ),md5函数定义中,函数接受的参数为字符串类型,那么如果传入的参数不是一个字符串而是一个数组的话,md5函数不会报错,只是无法计算出正确的结果,这样的话只要把数组传入md5()函数,计算出的结果都相等

1
2
3
4
5
6
7
8
<?php
$yzc = array(1,2,3);
$zzh = array(4,5,6);
var_dump(md5($yzc) == md5($zzh));
?>

可以看到md5()函数接受了非字符串类型的参数,但依旧进行了计算

(2)strcmp()函数(php的版本的小于5.3):

PHP手册中strcmp()函数的描述是:int strcmp ( string $str1 , string $str2 ) ,从该函数的定义可以看出需要向strcmp()函数传递两个字符串类型的变量。

strcmp()函数的功能是比较两个字符串,本质是将两个字符串转换成ACIll值,然后根据计算结果返回值,如果 $str1 小于 $str2 返回-1;如果相等,返回0;否则返回1

那么如果传入一个strcmp()函数无法接受的参数,比如数组,参数的类型不符合函数定义的类型,所以一旦传入,函数将发生错误,返回null。null 在前文讲过和 false 相等,就可以在比较中被认为是0

1
2
3
4
5
6
<?php
$yzc = array(1,2,3);
var_dump($yzc,'123');
?>

(3)switch()函数:

switch()函数如果为数字型case判断时,会将传入switch()函数的参数转换为int类型再进行case判断

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$yzc ="2zzh";
switch ($yzc) {
case 0:
case 1:
case 2:
echo "yzc is less than 3 but not negative";
break;
case 3:
echo "yzc is 3";
}
?>

(4)in_array()函数和array_search()函数

PHP手册中in_array()函数的描述:bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

PHP手册中array_search()函数的描述:mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )

以上两个函数的作用都是判断 $needle 变量是否存在于 $haystack 变量中,这两个函数还设置了一个 $strict 变量,默认值为 false ,如果值为true,会对两个变量的类型进行判断。

在这两个函数中,进行查询也可以理解为判断 $needle == $haystack,这样的话如果$needle$haystack分别是字符串类型和数值类型,就有可能使函数返回相等的显示

1
2
3
4
5
<?php
$array=[0,1,2,'3'];
var_dump(in_array('abc', $array)); //true
var_dump(in_array('1bc', $array)); //true
?>

字符串'abc'在比较时转换成了0,所以'abc'存在于$array
字符串'1abc'在比较时转换成了1,所以'1abc'存在于$array

0x04 实战中需要注意的地方:

1.md5()函数会产生0e\d+结构的字符串,前文说过0e\d+结构的字符串使用 == 比较都是相等的;但是如果使用 === 进行比较的话0e\d+结构的字符串是不相等的

1
<?php $flag = "This is flag!"; echo var_dump(md5($_GET['a'])).'<br>'; echo var_dump(md5($_GET['b'])).'<br>'; echo var_dump(md5($_GET['a']) == md5($_GET['b'])).'<br>'; //true echo var_dump(md5($_GET['a']) === '0').'<br>'; //false if (isset($_GET['a']) and isset($_GET['b'])) { if ($_GET['a'] != $_GET['b']){ if (md5($_GET['a']) === md5($_GET['b'])) //这里需要注意,是`===`而不是`==` die('Flag: '.$flag); else print 'Wrong.<br>'; } } ?>

$_GET['a']=QNKCDZO&$_GET['b']=s214587387a,这两个变量经过md5后都是0e\d+形式的字符串,我们构造的URL为:http://ip/ceshi2.php?a=QNKCDZO&b=s214587387a,输出结果如下图,最后的回显是Wrong说明md5($_GET['a']) === md5($_GET['b'])这条语句并不成立,两个变量的经过md5函数后并不===,我个人觉得原因在于===比较时,除了值要相等以外,类型也要相等。在比较时先确定了两个变量的类型,均为字符串类型,当类型确定下之后比较值的时候就是比较这两个字符串是否相同,图中的结果显示是不相同的。

但是如果是md5($_GET['a']) == md5($_GET['b'])这样的比较,那么就相等,因为在不比较类型的情况下,0e\d+字符串会被解析为科学计数法,从而导致连个变量相等。

所以如果是===,那么就要利用内置函数的参数松散性,使用md5()函数无法接受的参数,就比如我传进两个不相等的数组,这样md5()函数无法计算出正确的结果,从而导致md5($_GET['a']) === md5($_GET['b'])

构造URL:http://ip/ceshi2.php?a[]=aaa&b[]=bbb

2.PHP中十六进制数绕过的实例

0x(十六进制的转换)开头的字符串会被解析成十进制再进行比较,所以0x开头的字符串与其十进制的字符串或者数值都是相等的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$number = $_GET['a'];
function noother_says_correct($number){
$one = ord('1'); //ord()函数:将参数转换成ASCII值
$nine = ord('9');
// Check all the input characters!
for ($i = 0; $i < strlen($number); $i++){ //以`$number`变量的长度进行循环
// 使得`$number`变量中的每一个字符的值都不能在`1-9`之间,如果在则返回错误值
$digit = ord($number{$i});
if ( ($digit >= $one) && ($digit <= $nine) ){
// Aha, digit not allowed!
return false;
}
}
//最后会对`$number`变量与`"3735929054"`字符串进行比较
return $number == "3735929054";
}
if(noother_says_correct($number) == true){
echo 'This is flag';
}

从这段代码可以看出要求$number变量的值不能在1-9之间,但是在函数最后$number变量要与"3735929054"字符串进行比较,相等返回true,否则返回false。只有返回结果为true才会输出flag,那么目标就是让$number变量与"3735929054"字符串相等,方法就是利用十六进制数进制转换,可以发现"3735929054"的十六进制为deadc0de,我们在这个字符串前加上0x这样的话在和"3735929054"进行比较时0xdeadc0de就换先进行进制转换成十进制数再进行比较。这个十六进制数也可以绕过函数对于不能存在1-9的限制

那些年学过的PHP黑魔法

format_list_numbered

(无)

  1. 1. PHP弱类型
    1. 1.0.1. 0x01 PHP弱类型的介绍
    2. 1.0.2. 0x02 弱类型中的类型转换
    3. 1.0.3. 0x03 内置函数的参数松散性
    4. 1.0.4. 0x04 实战中需要注意的地方:
vertical_align_top

Copyright © 2017 yzc's blog

Powered by Hexo & Theme - Vateral