众所周知,php是世界上最好的语言,所以php的一些特性还是需要了解的。
php是弱类型语言
- 强类型,就是强制数据类型定义。也就是说,一旦变量被指定了某个数据类型,如果不经过强制类型转换,那么它永远都是这个数据类型
- 弱类型,对数据类型要求不严格,可以让数据类型随意互相转换
更直接的说,强类型是两个不同类型的变量不能用同一块内存存储,而弱类型可以。
Java、.net、Python、C++等语言都是强类型语言,而php是弱类型语言。
“==”和”===”
“==”和”===”都是用来比较两个数值是都相等的操作符
不同的是,”===”相比于”==”更加严格,只有两边类型和值完全相同时才会为真。而”==”在两边类型不一致时,会自动转换类型。
“==” 使类型转换的规则:
- 字符和数字比较,字符会被转换成数字
1
2
3var_dump("aaa"==0); ==> bool(true)
'admin'被转换成数字,由于'admin'是字符串,转换失败,int('admin')=0,所以比较结果为true - 混合字符串转换成数字,看字符串的第一个字符
1
2var_dump("admin1"==0) ==> bool(false)
var_dump("1admin"==1) ==> bool(true) - 以aeb开头的字符串(a,b是数字)转换成数字时,会被当成科学计数法a×10^b^
1
2
3var_dump((int)"2e2") ==> int(200)
var_dump((int)"2e2s") ==> int(200)
var_dump("-2e2"==-200) ==> bool(true) - 以0x开头的字符串转换成数字类型时,会被当成16进制数字(php5.x)
1
2
3
4var_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
4var_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
4var_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 | strcmp($str1, $str2) 比较两个字符串是否相等 |
strcmp函数两个参数都是字符串,如果某个参数为数组,则函数会发生错误,函数将返回NULL并发出Warning(不影响运行)
一个例子:
1 |
|
这里如果我们传入password[]=a
,则strcmp
将发生错误而返回NULL,而NULL和0弱类型相等,所以这里就可以绕过登录验证了。
这里如果用===
则不会有这个问题。
substr和sha1函数
1 | substr($string, int $start[, int $length]) 返回字符串string由start和length参数指定的子字符串 |
1 | sha1($string) 返回 sha1 散列值字符串。 |
如果传入给substr,sha1的参数是数组则返回NULL
md5函数
1 | md5($str[, bool $raw_output = FALSE]) 以32字符十六进制数字形式返回散列值 |
md5函数与php弱类型相等
1 | if(md5($Username) == md5($password)) |
这里用到上面”==”中aeb
的知识点,0exxx在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0(0e后面的必须是数字)
比如:
1 | var_dump(md5("240610708")) ==> string(32) "0e462097431906509019562988736854" |
满足md5(xxx) => 0exxxx的字符串有:
1 | byGcY 0e591948146966052067035298880982 |
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 | echo md5("129581926211651571912466741651878684928") ==> �T0D��o#��'or'8 |
同样原理的还有hash("whirlpool", $str, true)
这个函数
1 | echo hash("whirlpool", '364383', true); |
array_serch和in_array
1 | array_serch($value, $array, $type) 在数组$array中查找一个键值$value |
1 | in_array($search, $array, $type) 搜索数组中是否存在指定的值 |
这两个函数第三个参数如果没有设置为true,则都相当于 == ,是弱类型判断相等。
1 | var_dump(in_array('2aa', [1,2,3,4,5])) ==> bool(true) |
switch
switch的判断相等是弱类型相等
1 | $a = '1a'; |
bool欺骗
当存在json_decode
和unserialize
的时候,部分结构会被解释成bool类型。
1 | $json_str = $_POST["json"]; |
我们这里传入{"user":true,"pass":true}
即可绕过。
1 | $json = '{"user":true,"pass":true}'; |
原理:bool类型的true可以和任意非空、非零字符串相等
unserialize也是相同原理:
1 | $data = unserialize($_POST['unserialize_str']); |
传入a:2:{s:4:"user";b:1;s:4:"pass";b:1;}
即可绕过
1 | $data = unserialize('a:2:{s:4:"user";b:1;s:4:"pass";b:1;}'); |