PHP弱数据类型安全

     由于PHP的弱数据类型特性,造就了PHP的易学和易用。PHP在使用双等号(==)判断时,不会严格检验传入的变量类型,同时在执行过程中可以将变量自由地进行转换类型。由于弱数据类型地特点,在使用双等号和一些函数时,会造成一定的安全隐患。
     在下面的代码中,当用户输入type=0时,会直接进入支付逻辑。

<?php
	$type = $_GET['type'];
	if ($type == 'pay'){	//用了双等号进行判断
		echo "这里是支付逻辑";
	}
	else{
		echo "这里是其他逻辑";
	}
?>

     建议使用三个等号(===)来判断变量值与类型是否完全相等。下面经过修改后的代码可以解决这个问题,防止用户传入type=0时执行支付逻辑。

<?php
	$type = $_GET['type'];
	if ($type === 'pay'){	//用了三个等号进行判断
		echo "这里是支付逻辑";
	}
	else{
		echo "这里是其他逻辑";
	}
?>

使用PHP进行判断的时候,为了避免安全漏洞,在使用弱类型机制的时候需要特别留意。下面代码是一些弱类型判断实例:

<?php
	var_dump(false == 0);	//执行结果bool(true)

	var_dump(false == '');	//执行结果bool(true)

	var_dump(false == '0');	//执行结果bool(true)

	var_dump(0 == '0');	//执行结果bool(true)

	var_dump(0 == '0xxx');	//执行结果bool(true)

	var_dump(0 == 'xxx');	//执行结果bool(true)
?>

Hash比较缺陷

     研发人员在对比Hash字符串的时候常常用到(==)、(!=)进行比较,如果Hash至以0e开头,后面都是数字,当与数字进行比较时,就会被解析成0x10^n,会被判断与0相等,使攻击者可以绕过某些系统逻辑。

var_dump('0e123456789' == 0);	//bool(true)

var_dump('0e123456789' =='0');	//bool(true)

var_dump('0e1234abcde' == '0');	//bool(false)

当密码经过散列计算后可能会以0e开头。下面示例在进行密码判断时可以绕过登录逻辑。

<?php
	$username = $_POST['username'];
	$password = $_POST['password'];
	$userinfo = getuserpass($username);
	//当userinfo中的密码以0e开头时,随意构造password即可登录系统
	if ($userinfo['password'] == md5($password)){//Hash比较缺陷
		echo '登录成功';
		}
	else{
		echo '登录失败';
		}
?>	

使用hash_equals()函数比较Hash值,可以避免对比被恶意绕过。hash_equals()函数要求提供的两个参数必须是相同长度的字符串,如果所提供的字符串长度不同,会立即返回false。

<?php
	$username = $_POST['username'];
	$password = $_POST['password'];
	$userinfo = getuserpass($username);
	//使用hash_equals()函数进行严格的字符串比较
	if (hash_equals($userinfo['password'],md5($password)){
		echo '登录成功';
		}
	else{
		echo '登录失败';
		}
?>

     hash_equals()函数在PHP5.6中得到支持,如果系统版本号低于5.6,建议进行自定义实现该函数,代码如下:

if (!function_exists('hash_equals ')){
	function hash_equals($a, $b){
		if (!is_string($a)	|| !is_string($b)){
			return false;
		}
		$len = strlen($a);
		if ($len !== strlen($b)){
			return false;
		}
		$status = 0;
		for ($i=0; $i<$len; $i++){
			$status |= ord($a[$i]) ^ ord($b[$i]);
		}
		return $status === 0;
		}
	}
?>

bool比较缺陷
     在使用json_decode()函数或unserialize()函数时,部分结构被解释成bool类型,也会造成缺陷。
jason_decode示例:

<?php
	$str = '{"user":true,"pass":true}';
	$date = jason_decode($str,true);
	if ($data['user'] == 'root' && $data['pass'] == 'mypass'){
		print_r('登录成功! ' . "\n");
	}
	else{
		print_r('登录失败! ' . "\n");
	}
?>

执行结果:登录成功!
unserialize示例代码如下:

<?php
	$str = 'a:2:{s:4:"user";b:1;s:4:"pass";b:1;}';
	$data = userialize($str);
	if ($data['user'] == 'root' && $data['pass'] == 'mypass'){
		print_r('登录成功!' . "\n");
	}
	else{
		print_r('登录失败!' . "\n");
	}
?>

执行结果:登录成功!
比较容易出现问题的做法就是将数据系列化后放入浏览器的Cookie中,将用户信息保存在Cookie中十分不安全。避免bool比较隐患的作法是,严格判断数据是否相等的时候使用绝对相当——三个等号(===)。代码修改如下:

<?php
	$str = '{"user":true,"pass":true}';
	$data = json_decode($str,true);
	//修改为绝对相等
	if ($data['user'] === 'root' && $data['pass'] === 'mypass'){
		print_r('登录成功! ' . "\n");
	}
	else{
		print_r('登录失败! ' . "\n");
	}
?>

执行结果:登录失败!

<?php
	$str = 'a:2:{s:4:"user";b:1;s:4:"pass";b:1;}';
	$data = userialize($str);
	if ($data['user'] == 'root' && $data['pass'] == 'mypass'){
		print_r('登录成功!' . "\n");
	}
	else{
		print_r('登录失败!' . "\n");
	}
?>

数字转换比较缺陷
     当赋值给PHP变量的整型超过PHP的最大值PHP_INT_MAX时,PHP将无法计算出正确结果,攻击者可能会利用其跳过某些校验逻辑,如密码校验(无须输入正确的密码就可以直接登录用户的账号)、账号充值校验(充值很小的金额就可以进行巨额资金入账)等。下面吗示例演示超过PHP的最大值。

<?php
	$a = 99887766554433221100112233445566778899;
	$b = 99887766554433221100112233445566779999;
	$aa = '99887766554433221100112233445566778899';
	$bb = '99887766554433221100112233445566779999';
	var_dump($a === $b);	//输出bool(true)
	var_dump($a % 100);	//输出int(0)
	var_dump($b % 100);	//输出int(0)
	var_dump($aa === $bb);	//输出bool(false)
	var_dump($aa % 100);	//输出int(47)
	var_dump($bb % 100);	//输出int(47)
?>

     在实际的业务逻辑中(如充值金额,订单数量),一定要对最大值进行限制,避免数据越界而导致错误的执行结果。下面时一段商品购买的示例代码,其中对传入的价格和购买数量进行了范围校验,可避免数据越界产生错误的结果。

<?php
	$price = $_GET['price'];
	$num = $_GET['num'];
	if ($price <= 0){
		die("价格必须为整数");
	}
	if ($price > PHP_INT_MAX){
		die("价格不能超出系统最大限制");
	}
	if ($num <= 0){
		die("购买数量必须为整数");
	}
	if ($num > PHP_INT_MAX){
		die("购买数量不能超出系统最大限制");
	}
?>

     当赋值给PHP变量超长浮点数时,PHP的结果也将出现错误。在下面的代码示例中,当uid=0.99999999999999999时,代码逻辑会正常进入if语句,查询出uid=1的用户信息。以此类推,1.99999999999999999将会跳入$uid==”2″的判断中

<?php
	$uid = $_GET['uid'];
	if ($uid == "1"){
		$uid == intval($uid);
		$query = "select * from 'users' where uid=$uid;";
	}
	$result = mysql_query($query) or die(mysql_error());
	print_r(mysql_fetch_row($result));
?>

     在使用变量时先校验所传入的数据类型是否符合预期,如果超出预期,应该终止系统逻辑执行,避免浮点数在转换成整数时发生意外情况。下面是修复后的代码:

<?php
	$uid = $_GET['uid'];
	if (!is_int($uid)){
		die("传入的uid数据类型错误,系统已终止");
	}
	if ($uid == "1"){
		$uid = intval($uid);
		$query = "select * from 'users' where uid = $uid;";
	}
	$result = mysql_query($query) or die(mysql_error());
	print_r(mysql_fetch_row($result));
?>

     在代码中添加is_int($uid)判断传入的变量是否为整数,如果不是整数,则终止程序的进行。

switch比较缺陷

     当在switch中使用case判断数字时,switch会将其中的参数转换为int类型进行计算,如以下代码所示:

<?php
	$num = "2hacker";
	switch ($num){
		case 0:	echo "say none hacker!";
		break;
		case 1: echo "say one hacker!";
		break;
		case 2: echo "say two hackers!";
		break;
		default: echo "I don't know";
	}
?>

最终执行结果为:say two hacker!
     在进入switch逻辑前一定哟啊判断数据的合法性,对不合法的数据要进行及时阻断,防止恶意攻击者越过逻辑,出现逻辑错误。

<?php
	$num = "2hacker";
	if (!is_numeric($num)){
		die("错误的数据类型");
	}
	switch ($num){
		case 0:	echo "say none hacker!";
		break;
		case 1: echo "say one hacker!";
		break;
		case 2: echo "say two hackers!";
		break;
		default: echo "I don't know";
	}
?>

最终结果为:错误的数据类型,禁止访问

数组比较缺陷
     当使用in_array()或array_search()函数时,如果$strict参数没有设置为true,则in_array()或array_search()将使用松散比较来判断$needle是否在$haystack中。

<?php
	bool in_array(mixed $needle ,array $haystack[, bool $strict = false ])	//strict默认为false
	mixed array_search(mixed $needle, array $haystack [, bool $strick = false ])	//默认为false
?>

     下面是in_array()或array_search()函数在没有设置$strict参数时的执行结果。

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

     建议在使用将$strict的值设置为true,这样in_array()或array_search()就会严格地比较$needle的类型与$haystack中的类型是否相同,以避免一些安全问题,下面是修复后的代码.

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

文章版权声明

 1 原创文章作者:Z,如若转载,请注明出处: https://www.52hwl.com/35289.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年7月15日 上午9:18
下一篇 2023年7月15日 上午9:18