php弱类型以及一些函数的特点

众所周知,php是世界上最好的语言,所以php的一些特性还是需要了解的。

php是弱类型语言

  • 强类型,就是强制数据类型定义。也就是说,一旦变量被指定了某个数据类型,如果不经过强制类型转换,那么它永远都是这个数据类型
  • 弱类型,对数据类型要求不严格,可以让数据类型随意互相转换

更直接的说,强类型是两个不同类型的变量不能用同一块内存存储,而弱类型可以。

Java、.net、Python、C++等语言都是强类型语言,而php是弱类型语言。

“==”和”===”

“==”和”===”都是用来比较两个数值是都相等的操作符

不同的是,”===”相比于”==”更加严格,只有两边类型和值完全相同时才会为真。而”==”在两边类型不一致时,会自动转换类型。

“==” 使类型转换的规则:

  • 字符和数字比较,字符会被转换成数字
    1
    2
    3
    var_dump("aaa"==0);   ==> bool(true)

    'admin'被转换成数字,由于'admin'是字符串,转换失败,int('admin')=0,所以比较结果为true
  • 混合字符串转换成数字,看字符串的第一个字符
    1
    2
    var_dump("admin1"==0)   ==> bool(false)
    var_dump("1admin"==1) ==> bool(true)
  • 以aeb开头的字符串(a,b是数字)转换成数字时,会被当成科学计数法a×10^b^
    1
    2
    3
    var_dump((int)"2e2")   ==> int(200)
    var_dump((int)"2e2s") ==> int(200)
    var_dump("-2e2"==-200) ==> bool(true)
  • 以0x开头的字符串转换成数字类型时,会被当成16进制数字(php5.x)
    1
    2
    3
    4
    var_dump(0x123)          ==> int(291)
    var_dump((int)'0x123') ==> int(0)
    var_dump('0x123'==0x123) ==> php5.x: bool(true) php7.x: bool(false)
    var_dump('0x123'==291) ==> php5.x: bool(true) php7.x: bool(false)
  • bool类型的true可以和任意非空、非零字符串相等
    1
    2
    3
    4
    var_dump('0'==true)  ==> bool(false)
    var_dump(''==true) ==> bool(false)
    var_dump('0a'==true) ==> bool(true)
    var_dump('a'==true) ==> bool(true)
  • bool类型的true可以和任意非零数字相等
    1
    2
    3
    4
    var_dump(0==true)     ==> bool(false)
    var_dump((1-1)==true) ==> bool(false)
    var_dump(1==true) ==> bool(true)
    var_dump(100==true) ==> bool(true)

内置函数的松散性

strcmp函数

1
2
3
4
5
6
strcmp($str1, $str2) 比较两个字符串是否相等

返回值:
- 如果 str1 小于 str2 返回 < 0
- 如果 str1 大于 str2 返回 > 0
- 如果两者相等,返回 0

strcmp函数两个参数都是字符串,如果某个参数为数组,则函数会发生错误,函数将返回NULL并发出Warning(不影响运行)

一个例子:

1
2
3
4
5
6
7
8
9
10
11
<?php
$password = "xxxxx";

if(isset($_POST['password']))
{
if(strcmp($_POST['password'], $password)==0)
{
echo "login success";
}else die("wrong password");
}
?>

这里如果我们传入password[]=a,则strcmp将发生错误而返回NULL,而NULL和0弱类型相等,所以这里就可以绕过登录验证了。

这里如果用===则不会有这个问题。

substr和sha1函数

1
2
3
substr($string, int $start[, int $length])  返回字符串string由start和length参数指定的子字符串

返回提取的子字符串,或者在失败时返回 FALSE
1
sha1($string)  返回 sha1 散列值字符串。

如果传入给substr,sha1的参数是数组则返回NULL

md5函数

1
2
3
4
md5($str[, bool $raw_output = FALSE])  以32字符十六进制数字形式返回散列值

- raw_output
如果可选的raw_output被设置为TRUE,那么将以16字节长度的原始二进制格式返回

md5函数与php弱类型相等

1
2
3
4
if(md5($Username) == md5($password)) 
{
echo "success";
}

这里用到上面”==”中aeb的知识点,0exxx在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0(0e后面的必须是数字)

比如:

1
2
3
var_dump(md5("240610708"))   ==> string(32) "0e462097431906509019562988736854"    
var_dump(md5('QNKCDZO')) ==> string(32) "0e830400451993494058024219903391"
var_dump(md5("240610708") == md5('QNKCDZO')) ==> bool(true)

满足md5(xxx) => 0exxxx的字符串有:

1
2
3
4
5
6
7
8
9
10
11
byGcY         0e591948146966052067035298880982
sonZ7y 0e463306343746311593316642162425
QNKCDZO 0e830400451993494058024219903391
s878926199a 0e545993274517709034328855841020
s155964671a 0e342768416822451524974117254469
s214587387a 0e848240448830537924465865611904
s214587387a 0e848240448830537924465865611904
s878926199a 0e545993274517709034328855841020
s1091221200a 0e940624217856561557816327384675
s1885207154a 0e509367213418206700842008763514
... ... ... ...

md5传入数组

和上面几个函数一样,md5函数传入数组时会发生错误而返回Null

1
var_dump(md5([1,2])==md5([2,3]))  ==> bool(true)

md5第二个参数为true的情况

当md5函数的第二个参数为true时,该函数的输出是原始二进制格式,此时如果md5用于sql语句中,就存在漏洞
比如

1
$sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'"

而如果md5函数返回的字符串是xxx’or’{true}xxx的格式的,则可以绕过验证而成功登录。

很巧,正有几个字符串可以满足这个条件:

1
2
echo md5("129581926211651571912466741651878684928")  ==> �T0D��o#��'or'8
echo md5("ffifdyop") ==> 'or'6�]��!r,��b

同样原理的还有hash("whirlpool", $str, true)这个函数

1
2
3
echo hash("whirlpool", '364383',  true);
==> T�N�F� �/$=/!�%.i��<�0��'='��IT ��Q
r=���

array_serch和in_array

1
2
3
4
5
array_serch($value, $array, $type)  在数组$array中查找一个键值$value
- 如果找到了该值,匹配元素的键名会被返回。
- 如果没找到,则返回false

- $type 可选。如果该参数被设置为 true,则函数在数组中搜索数据类型和值都一致的元素。默认为false
1
2
3
4
in_array($search, $array, $type)  搜索数组中是否存在指定的值
如果数组中存在指定的值,返回true,否则返回false

- $type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。默认为false

这两个函数第三个参数如果没有设置为true,则都相当于 == ,是弱类型判断相等。

1
2
3
4
5
var_dump(in_array('2aa', [1,2,3,4,5]))           ==> bool(true)
var_dump(array_search('2aa', [1,2,3,4,5])) ==> int(1)

var_dump(in_array('2aa', [1,2,3,4,5], true)) ==> bool(false)
var_dump(array_search('2aa', [1,2,3,4,5], true)) ==> bool(false)

switch

switch的判断相等是弱类型相等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$a = '1a';
switch ($a) {
case 1:
echo "1";
break;
case 2:
echo "2";
break;
case 3:
echo "3";
break;
default:
echo "default";
break;
}
结果: 1

bool欺骗

当存在json_decodeunserialize的时候,部分结构会被解释成bool类型。

1
2
3
4
5
6
7
8
$json_str = $_POST["json"];

$data = json_decode($json_str, true);

if ($data['user'] == 'admin' && $data['pass']=='secirity')
{
echo 'logined in as bool'."\n";
}

我们这里传入{"user":true,"pass":true}即可绕过。

1
2
3
4
$json = '{"user":true,"pass":true}';
$data = json_decode($json, true);
var_dump($data['user'] == "admin"); ==> bool(true)
var_dump($data['pass'] == "secirity"); ==> bool(true)

原理:bool类型的true可以和任意非空、非零字符串相等

unserialize也是相同原理:

1
2
3
4
5
$data = unserialize($_POST['unserialize_str']);
if ($data_unserialize['user'] == 'admin' && $data_unserialize['pass']=='secirity')
{
echo 'logined in unserialize'."\n";
}

传入a:2:{s:4:"user";b:1;s:4:"pass";b:1;}即可绕过

1
2
3
$data = unserialize('a:2:{s:4:"user";b:1;s:4:"pass";b:1;}');
var_dump($data['user'] == "admin"); ==> bool(true)
var_dump($data['pass'] == "secirity"); ==> bool(true)
文章作者: Dar1in9
文章链接: http://dar1in9s.github.io/2020/04/03/php/php弱类型/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dar1in9's Blog