目录
==和===
MD5函数
intval
strpos
in_array
preg_match
str_replace
==和===
使用 ==
时,如果两个比较的操作数类型不同,PHP 会尝试将它们转换为相同的类型,然后再进行比较。
使用 ===
进行比较时,不仅比较值,还比较变量的类型,PHP 不会进行任何类型强制转换。如果两个操作数的类型不同,即使它们的值看起来相同,比较结果也会为假。
在 PHP 中,强制类型转换是一种显式地将一个变量从一种类型转换为另一种类型的方式。当需要将一个变量转换为特定类型时,可以使用强制类型转换。以下是 PHP 中常见的强制类型转换规则:
- 转换为布尔值 (bool)
- 当转换为布尔值时,以下值被认为是
false
:
- 布尔值
false
本身- 整数
0
- 浮点数
0.0
- 空字符串
""
- 字符串
"0"
- 没有设置任何值的变量(
null
)- 没有元素的数组
- 特殊类型
null
- 没有任何属性的对象
- 所有其他值都被认为是
true
。- 转换为整数 (int)
- 当转换为整数时,浮点数会被截断为整数部分,字符串从开头开始解析直到第一个非数字字符为止。
- 布尔值
true
转换为1
,false
转换为0
。null
转换为0
。- 对象转换为整数时,会调用对象的
__toString()
方法(如果存在),然后将结果转换为整数。- 转换为浮点数 (float)
- 当转换为浮点数时,字符串从开头开始解析直到第一个非数字字符或字符串结束。
- 布尔值
true
转换为1.0
,false
转换为0.0
。null
转换为0.0
。- 对象转换为浮点数时,会调用对象的
__toString()
方法(如果存在),然后将结果转换为浮点数。- 转换为字符串 (string)
- 整数和浮点数会被转换为它们的字符串表示形式。
- 布尔值
true
转换为"1"
,false
转换为""
(空字符串)。- 数组和对象转换为字符串时,会调用它们的
__toString()
方法(如果存在)。如果不存在,则会触发一个警告,并返回null
的字符串表示形式。null
转换为""
(空字符串)。- 资源类型(resource)在转换为字符串时通常没有意义,除非与特定的资源处理器有关。
- 转换为数组 (array)
- 转换为数组时,对于非数组和对象类型的值,将创建一个只包含该值的数组。
- 对于对象,如果对象实现了
ArrayAccess
接口,它将被视为数组。否则,将创建一个包含对象属性的数组。- 转换为对象 (object)
- 在 PHP 中,将一个值转换为对象通常需要该值是一个已经存在的类名或者一个实现了
__invoke()
方法的对象。- 如果使用类名,将创建一个该类的实例。
- 如果使用实现了
__invoke()
方法的对象,将调用该对象的__invoke()
方法。- 转换为 null
- 将一个值设置为
null
是一种特殊的强制类型转换,它将变量设置为没有值的状态。
测试代码,参数x用于==的测试,参数y用于===的测试:
//1、== ===缺陷绕过 == 弱类型对比 ===还会比较类型
$a=1;
if($a==$_GET['x']){
echo $flag;
}
//1.0 +1 1a
$a='1';
if($a===$_GET['y']){
echo $flag;
}
测试用例中,传入x=+1 / x=1.0 / x=1sfd等都会与变量a相等,因为==会进行强制转换之后再比较
而使用===必须类型一致,传入y=1.0 / y=1
MD5函数
在 PHP 中,md5()
函数的一个已知缺陷是所谓的“0e”碰撞。这个问题特定于 PHP 的字符串比较机制,而不是 md5()
函数本身。当使用 ==
运算符比较两个字符串时,如果字符串以“0e”开头,后面跟着一系列数字,PHP 会将这些字符串解释为科学记数法表示的浮点数,并进行数值比较而不是字符串比较。
//2、MD5函数缺陷绕过 ==弱对比 ===强类型对比
if($_GET['name'] != $_GET['password']){
if(MD5($_GET['name']) == MD5($_GET['password'])){
echo $flag;
}
echo '?';
}
根据代码逻辑,先判断传入的name和password不相等,再判断name和password讲过md5加密后相等,才会输出flag,否则输出问号
测试用例会用到以下两个字符串
QNKCDZO以MD5加密:0e830400451993494058024219903391
240610708以MD5加密:0e462097431906509019562988736854
正常情况下,如name=test&password=123456,md5加密是不相等的
而当我们使用上面两个用例字符串,md5加密后php会判断相等
当你尝试对数组使用 md5()
函数时,PHP 会发出一个警告,告诉你 md5()
期望的是一个字符串,而你提供的是一个数组。然后,由于无法对数组进行哈希计算,它会返回 null
。在使用===比较时,传入两个数组比较,等于null===null返回true
//2、MD5函数缺陷绕过 ==弱对比 ===强类型对比
if($_GET['name'] != $_GET['password']){
if(MD5($_GET['name']) === MD5($_GET['password'])){
echo $flag;
}
echo '?';
}
上面解释过产生0e碰撞是因为php当作了科学计数比较,而使用 ===
进行字符串比较会同时比较值和类型,因此不会受到上述“0e”碰撞问题的影响。
这种情况可以使用传入数组的方式绕过,name[]=1和password[]=2显然满足两者不相等这个条件,而md5不对数组哈希,并报Warning返回null,所以null===null返回true
intval
intval( $var, $base )是 PHP 中的一个内置函数,用于获取变量的整数值,默认是十进制。这个函数尝试将给定的变量转换为整数类型。如果变量是字符串,那么它会解析字符串直到遇到一个非数字字符为止,然后返回该数字。如果字符串开头就是一个非数字字符,那么它会返回 0。
当使用 intval()
函数并设置基数(base)为 0 时,它会根据字符串的内容来尝试猜测其原始的数字基数,并进行相应的转换。这通常用于处理以特定前缀开头的数字字符串,如十六进制(以 "0x" 或 "0X" 开头)或八进制(以 "0" 开头),如果字符串不符合这些模式,它会被尝试解析为十进制数。
注意:如果字符串不是一个有效的八进制或十六进制数(即,如果它包含除了 0-7 之外的字符对于八进制,或者除了 0-9 和 a-f(不区分大小写)之外的字符对于十六进制),那么 intval()
将返回 0。
测试代码:
//3、intval缺陷绕过
$i='666';
$ii=$_GET['n'];
if(intval($ii)==$i){
echo $flag;
echo "<br>";
}
$i='666';
$ii=$_GET['n'];
if(intval($ii,0)==$i){
echo $flag;
}
使用666safs两个都能正常输出 ,都能正常截断后面的字符串,但改为十六进制0x29a则只有base等与0的才能正常识别输出
strpos
strpos()
是 PHP 中的一个字符串函数,用于查找一个子字符串在另一个字符串中首次出现的位置。
strpos(string $haystack, mixed $needle, int $offset = 0): int|false
参数说明:
$haystack
:必需。要搜索的字符串。$needle
:必需。要查找的字符串。如果该参数不是字符串,它会被转换为整数并视为字符的 ASCII 值。$offset
:可选。从$haystack
字符串中的哪个位置开始搜索。
返回值:
- 如果找到子字符串,则返回其第一次出现的位置的索引(基于0的索引)。
- 如果没有找到子字符串,则返回
false
。
测试代码:
//4、对于strpos()函数,我们可以利用换行进行绕过(%0a)
$i='666';
$ii=$_GET['h'];
if(strpos($ii,$i,0)){
echo $flag;
}
在这段代码中,如果h=666,并不会输出flag,因为strpos返回 666
在 $ii
中首次出现的位置的索引,而666
是字符串的开始,它将返回 0,0在php同样代表false
可以通过换行进行绕过:%0a666
%0a:表示换行
也可以在有必要的条件下使用 数组 返回null
//4、对于strpos()函数,我们可以利用换行进行绕过(%0a)
$i='666';
$ii=$_GET['h'];
if(strpos($ii,$i,0)===null){
echo $flag;
}
in_array
in_array()
是 PHP 中的一个函数,用于检查一个值是否存在于数组中。它的基本语法如下:
bool in_array ( mixed $search , array $array [, bool $strict = FALSE ] )
参数说明:
$search
:必需。要搜索的值。$array
:必需。要搜索的数组。$strict
:可选。如果设置为TRUE
,则还会检查$search
的类型是否和数组中的元素类型相同。默认为FALSE
。(True类似===,Flase类似==)
返回值:
- 如果在数组中找到值则返回
TRUE
,否则返回FALSE
。
测试代码:
//5、in_array第三个参数安全
$whitelist = [1,2,3];
$page=$_GET['i'];
if (in_array($page, $whitelist)) {
echo "yes";
}
preg_match
preg_match
是 PHP 中的一个函数,用于执行一个正则表达式匹配。如果匹配成功,它会返回 1;如果匹配失败或发生错误,它会返回 0;如果没有进行匹配,它会返回 false
。
preg_match
只能处理字符串,如果不按规定传一个字符串,传一个数组进入,就会使preg_match
失效,从而绕过该函数的检测。
测试代码:
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
从代码逻辑看,获取并判断num参数值是否为空,不为空则判断匹配num值中是否有0-9的字符,符合条件执行die输出nonono并杀死程序,否则执行下一个条件intval(num)取整操作,不为flase输出flag
正常输入数字,会杀死程序
当我们传一个数组,就能绕过正则的检测
str_replace
str_replace
是 PHP 中的一个函数,用于在字符串中查找并替换指定的值。它接受一个或多个搜索值(需要被替换的),以及对应的替换值,并在给定的主字符串中进行替换。
缺陷是无法迭代过滤,可以双写绕过
测试代码:
//7、str_replace无法迭代过滤
$sql=$_GET['s'];
$sql=str_replace('select','',$sql);
echo $sql;
这段代码会过滤掉select并替换为空
双写则可以绕过,他能过滤多个,但是二次合并的无法迭代的过滤 ,如sselectelect过滤一次后,再次合并的select不会被过滤