php+弱数据类型,PHP编码安全之一: 弱数据类型安全

a3ca1169dc0ad090dc9f445546313ce1.png

本文内容参考自《PHP安全之道》。

由于PHP的弱数据类型的特性, 造成了其易学和易用的特点。但是PHP在使用等于(==)判断的时候, 不会严格检查变量类型,会进行变量的自动转换,由此造成了一定的安全隐患。

在下面的代码中, 当用户输入的type=a时, 会直接进入支付逻辑:

$type = $_GET["type"];

if($type == 0){

echo "进入支付流程";

}else{

echo "其他逻辑";

}

这时我们建议使用全等于(===)来进行逻辑判断。

我们再看几个例子:

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 比较缺陷

MD4、MD5、SHA-1、SHA-256、SHA-384以及SHA-512,都是比较常见的安全领域的HASH应用。我们再对比Hash字符串的时候常常用到等于(==)、不等于(!=)进行比较。如果Hash值以0e开头, 后面都是数字, 当与数字进行比较时, 就会被解析成科学计数法 0 X 10^n, 会被判断与0相等, 使攻击者可以绕过某些系统逻辑。

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

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

var_dump("0e123456789abc" == "0"); // bool(false)

当密码经过散列计算后可能会以0开头, 看下面的例子:

$user_name = $_POST["user_name"];

$password = $_POST["password"];

$user_info = getUserInfo($user_name);

if($user_info["password"] == md5($password)){ //这里就可能遇到Hash比较缺陷造成系统漏洞

echo "登录成功";

}else{

echo "登录失败";

}

从PHP5.6开始, 提供了hash_equals函数来比较Hash值。hash_euqals函数要求2个参数必须是长度相同的字符串,否则返回false。上面的代码应该修改为:

.....

if(hash_equals($user_info["password"], md5($password)){ //这里就可能遇到Hash比较缺陷造成系统漏洞

echo "登录成功";

}else{

echo "登录失败";

}

.....

bool 比较缺陷

当使用json_decode或unserialize函数时,部分结构被解析成 bool 类型, 也会造程缺陷。

$arr = ["user"=>true, "pass"=>true];

$str = json_encode($arr); // {"user":true,"pass":true}

$data = json_decode($str, true);

if($data["user"] == "root" && $data["pass"] == "mypass"){

echo "login success";

}else{

echo "login failed";

}

执行结果为: "login success"。

unserialize的例子如下:

$arr = ["user"=>true, "pass"=>true];

$str = serialize($arr); // a:2:{s:4:"user";b:1;s:4:"pass";b:1;}

$data = unserialize($str, true);

if($data["user"] == "root" && $data["pass"] == "mypass"){

echo "login success";

}else{

echo "login failed";

}

执行结果为: "login success"

对于bool比较缺陷, 使用 全等(===)来避免。

数字转换缺陷

php的int和float作为标量(scalar)类型, 都有其最大最小值。

define ("PHP_INT_MAX", 9223372036854775807);

define ("PHP_INT_MIN", -9223372036854775808);

//从PHP7.2开始, 有定义float的最大最小值

define("PHP_FLOAT_MAX", 1.7976931348623e+308);

define("PHP_FLOAT_MIN", 2.2250738585072e-308);

如果变量的值超过规定的范围时将无法计算正确的结果。下面的例子中的$a、$b、$aa、$bb均超出了int的最大值:

$a = 92233720368547758079223372036854775807;

$b = 92233720368547758079223372036854775819;

$aa = "92233720368547758079223372036854775807";

$bb = "92233720368547758079223372036854775819";

var_dump(intval($a)); // int(0)

var_dump(intval($b)); // int(0)

var_dump(intval($aa)); // int(9223372036854775807)

var_dump(intval($bb)); // int(9223372036854775807)

var_dump($a == $b); // bool(true)

var_dump($a === $b); // bool(true)

var_dump($a % 100); // int(0)

var_dump($b % 100); // int(0)

var_dump($aa === $bb); // bool(true)

var_dump($aa % 100); // int(7)

var_dump($bb % 100); // int(7)

下面看一下float类型的例子:

$c = 0.9999999999999999999999999;

$d = 0.9999999999999999999999998;

$cc = "0.9999999999999999999999999";

$dd = "0.9999999999999999999999998";

var_dump(floatval($c)); // float(1)

var_dump(floatval($d)); // float(1)

var_dump(floatval($cc)); // float(1)

var_dump(floatval($dd)); // float(1)

在实际的业务逻辑中, 比如支付金额、转账金额, 一定要对最大最小值进行判断,避免数据格式不正确或者越界而导致意外。

建议对int、float类型参数进行empty、is_int/is_numeric判断。

Switch 比较缺陷

当switch中使用case判断数字时, switch会将参数转换为int类型进行比较:

$num = "2hacker";

switch($num){

case 0: echo "zero"; break;

case 1: echo "one"; break;

case 2: echo "two"; break;

default: echo "defualt";

}

执行的结果是: two

在进入switch之前一定要判断数据的合法性:

....

if(!is_int($num)){

die("错误的数据类型!");

}

....

Array 数组比较缺陷

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值