PHP编码安全:弱数据类型安全

admin 2021年12月15日03:15:46评论104 views字数 5999阅读19分59秒阅读模式

PHP编码安全:弱数据类型安全


微信公众号:计算机与网络安全

ID:Computer-network


由于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比较、bool比较、数字转换比较、switch比较、数组比较等几种比较方式常常被忽视。


1、Hash比较缺陷


研发人员在对比Hash字符串的时候常常用到等于(==)、不等于(!=)进行比较。如果Hash值以0e开头,后面都是数字,当与数字进行比较时,就会被解析成0×10n,会被判断与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)) { // Hase比较缺陷

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()函数在PHP 5.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;

}

}


2、bool比较缺陷


在使用json_decode()函数或unserialize()函数时,部分结构被解释成bool类型,也会造成缺陷,运行结果超出研发人员的预期。


json_decode示例代码如下。


<?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");

}


执行结果为:登录成功!


unserialize示例代码如下。


<?php

$str='a:2:{s:4:"user";b:1;s:4:"pass";b:1;}';

$data=unserialize($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=unserialize($str);

if($data['user']=='root' && $data['pass']=='myPass')

{

print_r('登录成功!'."n");

} else {

print_r('登录失败!'."n");

}


执行结果为:登录失败!

3、数字转换比较缺陷


当赋值给PHP变量的整型超过PHP的最大值PHP_INT_MAX时,PHP将无法计算出正确结果,攻击者可能会利用其跳过某些校验逻辑,如密码校验(无须输入正确的密码就可以直接登录用户的账号)、账号充值校验(充值很小的金额就可以进行巨额资金入账)等。下面代码示例中$a、$b、$aa、$bb均超出了PHP的最大值,所以运算结果超出了预期。


<?php

$a=92233720368547758079223372036854775807;

$b=92233720368547758079223372036854775819;

$aa='92233720368547758079223372036854775807';

$bb='92233720368547758079223372036854775819';

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(7)

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


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


<?php

$price=$_GET['price'];

$num=$_GET['num'];

if($price<=0) {

die(“价格必须为正数”);

}

if($price>PHP_INT_MAX) {

die(“价格不能超出系统最大限制”);

}

if($price<=0) {

die(“购买数量必须为正数”);

}

if($price>PHP_INT_MAX) {

die(“购买数量不能超出系统最大限制”);

}


当赋值给PHP变量超长浮点数时,PHP的结果也将出现错误。在下面的代码示例中,当uid=0.99999999999999999时,代码逻辑会正常进入if语句,查询出uid=0的用户信息。以此类推,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) ordie(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) ordie(mysql_error());

print_r(mysql_fetch_row($result));


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


4、switch比较缺陷


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


<?php

$num="2hacker";

switch($num) {

case0: echo "say none hacker!";

break;

case1: echo "say one hacker!";

break;

case2: echo "say two hacker!";

break;

default: echo "I don't know!";

}


最终执行结果为:say two hacker!


在进入switch逻辑前一定要判断数据的合法性,对不合法的数据要进行及时阻断,防止恶意攻击者越过逻辑,出现逻辑错误。


<?php

$num="2hacker";

if(!is_numeric($num)) {

die("错误的数据类型,禁止访问!");

}
switch($num) {

case0: echo "say none hacker!";

break;

case1: echo "say one hacker!";

break;

case2: echo "say two hacker!";

break;

default: echo "I don't know!";

}


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


5、数组比较缺陷


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


bool in_array(mixed $needle,array $haystack[,bool $strict=false]) // strict默认为false

mixed array_search(mixed $needle,array $haystack[,bool $strict=false]) // strict默认为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()就会严格地比较$needls的类型与$haystack中的类型是否相同,以避免一些安全问题。下面是修复后的代码。


<?php

$array=[0,1,2,'3'];

var_dump(in_array('abc',$array,true)); // false

var_dump(array_search('abc',$array,true)); // false

var_dump(in_array('1bc',$array,true)); // false

var_dump(array_search('1bc',$array,true)); // false


来源:计算机与网络安全

相关推荐: Java Web安全之代码审计(总结的很全)

信息安全的75%发生在Web应用而非网络层。本文内容主要以Java Web安全-代码审计为中心展开。文章内容很全,1万8千字,建议收藏本文目录一、JavaWeb 安全基础1. 何为代码审计?2. 准备环境和辅助工具二、反编译技巧1. Java类编译与反编译基础…

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月15日03:15:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PHP编码安全:弱数据类型安全http://cn-sec.com/archives/676576.html

发表评论

匿名网友 填写信息