来源 | 代码审计(ID:white-hat-note)

经历了近半年的alpha版本测试后,PHP在2020年11月26号正式发布了8.0版本:https://www.php.net/releases/8.0/en.php

今天我们就来浏览一下PHP 8.0中出现的主要特性,以及它给我们安全研究人员带来的挑战。

命名参数 Named Arguments

PHP 8 以前,如果我们需要给一个函数的第N个参数传参,那么这个参数前面的所有参数,我们都需要传参。但是实际上有些参数是具有默认值的,这样做显得多此一举。

比如,我们要给htmlspecialchars的第4个参数传递false,在PHP 8 以前需要传入4个参数:

htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

在8.0以后增加了命名参数,我们只需要传递必需的参数和命名参数即可,方便了很多:

htmlspecialchars($string, double_encode: false);

属性注释 Attributes

属性注释是我自己取得名字,在英文原文中是单词「Attributes」(在C++、C#、Rust里也是相同的单词,但翻译有些差别)。这个新语法有点类似Python里的修饰器,以及Java里的Annotation。

但是,PHP里Attributes的作用还是更偏向于替换以前的doc-block,用于给一个类或函数增加元信息,而不是类似Python的修饰器那样,可以动态地劫持函数的输入与输出。

属性注释的简单例子:

#[ListensTo('error')]
function onerror() {// do something
}

上面这个例子实际测试你会发现,属性注释里的东西也真的只是一个注释,执行上述的代码也不会去调用ListensTo类。这也印证了上面所说的,Attributes只是对以前doc-block的一个接纳,而非创造了一种HOOK函数的方式。

如果你需要执行Attributes里面的代码,仍然需要通过反射来做到,比如:

#[Attribute]
class ListensTo {public string $event;function __construct($event) {$this->event = $event;}
}#[ListensTo('error')]
function onerror()
{// do something
}$listeners = [];
$f = new ReflectionFunction('onerror');
foreach($f->getAttributes() as $attribute) {$listener = $attribute->newInstance();$listeners[$listener->event] = $f;
}

我模拟了一个设计模式中监听模式的事件处理方法注册过程,相比于以前解析Doc-Block的过程,这个流程要更加简单。

相比于其他的新特性,框架或IDE的设计者可能会研究的更深,普通开发者只需要按照框架的文档简单使用这个语法即可。

构造器属性提升 Constructor property promotion

这是一个利国利民的好特性,可以延长键盘的寿命……PHP 8以前,我们定义一个类时,可能会从构造函数里接收大量参数并赋值给类属性,如:

class Point {public float $x;public float $y;public float $z;public function __construct(float $x = 0.0,float $y = 0.0,float $z = 0.0,) {$this->x = $x;$this->y = $y;$this->z = $z;}
}

实际上这已经形成了一种范式,我们要不厌其烦地进行定义->传递->赋值的过程。PHP 8以后给出了一种更加简单的语法:

class Point {public function __construct(public float $x = 0.0,public float $y = 0.0,public float $z = 0.0,) {}
}

直接在构造函数的参数列表位置完成了类属性的定义与赋值的过程,减少了大概三分之二的代码量。

另外提一句,这个RFC的作者是Nikita Popov,也就是著名的开源项目PHP-Parser的作者,做PHP代码分析的同学应该经常和这个项目打交道。他今年去了PHPStorm团队,相信这个老牌IDE在Nikita的加持下会变得更加好用。

联合类型 Union types

PHP 8 以前的Type Hinting,只支持使用一个具体的Type,比如:

function sample(array $data) {var_dump($data);
}

这个功能鸡肋的一点是,有些地方接受参数类型可能有多个类型,或者支持传入null。

在7.1时解决了null的问题:

function sample(?array $data) {var_dump($data);
}

但是仍然无法指定多个类型hint。

PHP 8 中总算支持了Union types,我们可以通过|来指定多个类型Hint了:

function sample(array|string|null $data) {var_dump($data);
}

Match 语法

这是一个新的关键字match,这也是一个利国利民的好特性,又一次延长了键盘的寿命……

在PHP 8.0以前,我们要根据一个名字来获取一个值,通常需要借助switch或者数组,比如:

switch ($extension) {case 'gif':$content_type = "image/gif";break;case 'jpg':$content_type = "image/jpeg";break;case 'png':$content_type = "image/png";break;
}
echo $content_type;

现在可以简化成一个「表达式」:

echo match ($extension) {'gif' => "image/gif",'jpg' => "image/jpeg",'png' => "image/png"
};

Null安全的操作符 Nullsafe operator

这又又又是一个利国利民的好特性,又又又一次延长了键盘的寿命……

在PHP 8以前,如果封装的较多,我们经常出现一种情况:一个函数接受X对象,但又可能是null,此时我在使用X对象属性前,就需要对null进行判断,以免出现错误。

在对象较多时,容易出现多层嵌套判断的情况,比如:

$country =  null;if ($session !== null) {$user = $session->user;if ($user !== null) {$address = $user->getAddress();if ($address !== null) {$country = $address->country;}}
}

PHP 8 以后增加了一个新语法:?->,非常类似于PHP7里引入的??。就是在取属性前,PHP会对对象进行判断,如果对象是null,那么就直接返回null了,不再取其属性:

$$country = $session?->user?->getAddress()?->country;

字符串数字弱类型比较优化

这一个改动可能会对安全漏洞挖掘的影响较大。PHP 8 以前,在使用==比较或任何有弱类型转换的情况时,字符串都会先转换成数字,再和数字进行比较。

比如,这个代码在PHP 8以前的结果是true和0,在PHP 8以后得到的则是false和1:

var_dump('a' == 0);
switch ('a') {case 0:echo 0;break;default:echo 1;break;
}

老的弱类型可能会有什么安全问题呢?我曾经挖掘到的一个真实案例,大概代码是这样:

$type = $_REQUEST['type'];
switch ($type) {case 1:$sql = "SELECT * FROM `type_one` WHERE `type` = {$type}";break;case 2:$sql = "SELECT * FROM `type_two` WHERE `type` = {$type}";break;default:$sql = "SELECT * FROM `type_default`";break;
}

开发者认为$type是1和2的时候才会进入SQL语句拼接中,但实际我们传入1 and 1=2即可进入case 1,导致SQL注入漏洞。

PHP 8以后彻底杜绝了这种漏洞的产生。

内部函数严格参数检查

在PHP 8 以前,如果我们使用内部函数时传入的参数有误(比如,参数类型错误,参数取值错误等),有时会抛出一个异常,有时是一个错误,有时只是一个警告。在PHP 8 以后,所有这类错误都将是一个异常,并且导致解释器停止运行,比如:

strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given
array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0

这个改动可能会影响一些安全漏洞的利用,有一些我们之前通过弱类型等tricks构造的POC,在老版本PHP中只是一个警告,不会影响解释器的执行,但8.0之后将会导致错误,也就中断了执行。

JIT

JIT(Just-In-Time)被鸟哥称为PHP 8 中最重要的改动,我来简单介绍一下PHP 8 的JIT。

PHP 8 的JIT附加在opcache这个扩展中,opcache本身就是对PHP解释器的优化。没有使用opcache时,PHP解释器是在运行PHP脚本的时候进行“编译->Zend虚拟机执行”的过程。而opcache的出现实际上就是节省了编译的时间,代码在第一次运行时会编译成opcache能识别的缓存(opcode),之后运行时就免除了编译的过程,直接执行这段opcode。

而JIT的出现再次优化了这个过程,JIT会将一些opcode直接翻译成机器码。这样PHP解释器在执行时,如果发现缓存中保存的是机器码,就会直接交给CPU来执行,又减少了Zend虚拟机执行opcode的时间。

普通开发者可能对JIT比较无感,毕竟大家的性能瓶颈多半出现在IO等问题中,但对于性能要求极高的人或企业来说,JIT的确是对PHP的重要改进。

其他可能和安全相关的改动

作为安全研究者,我会更关注的是和安全相关的改动。除了前面提到了弱类型方面的改动外,PHP 8还进行了如下一些和安全相关的改动:

  • assert()不再支持执行代码,少了一个执行任意代码的函数,这个影响还是挺大的。

  • create_function()函数被彻底移除了,我们又少了一个可以执行任意代码的函数。

  • libxml依赖最低2.9.0起,也就是说,XXE漏洞彻底消失在PHP里了。

  • 继preg_replace()中的e模式被移除后,mb_ereg_replace()中的e模式也被彻底移除,再次少了一个执行任意代码的函数。

  • Phar中的元信息不再自动进行反序列化了,phar://触发反序列化的姿势也告别了。

  • parse_str()必须传入第二个参数了,少了一种全局变量覆盖的方法。

  • php://filter中的string.strip_tags被移除了,我在文章《谈一谈php://filter的妙用》中提到的去除死亡exit的方法之一也就失效了。

  • strpos()等函数中的参数必须要传入字符串了,以前通过传入数组进行弱类型利用的方法也失效了。

这些改动,改的我心拔凉拔凉的……我一度认为PHP核心团队里混入了安全研究者,为什么我们常用的小trick都被改没了呢?

总结

总结一下PHP 8,我只有两个感想:

  • 我不用担心键盘的寿命了,但是我的头顶变凉了

  • 比头顶更凉的是我的心,安全真是越来越难做了

好在,现在很多人慢慢转战Java,Java可以吃的饭应该还有很多。

参考链接:

  • https://www.php.net/releases/8.0/en.php

  • https://wiki.php.net/rfc/attributes_v2

  • https://wiki.php.net/rfc/shorter_attribute_syntax

  • https://stitcher.io/blog/attributes-in-php-8

  • https://www.laruence.com/2020/06/27/5963.html

更多精彩推荐
☞什么?性能强大的 M1 芯片不支持 Docker ?
☞“那个工作 10 年没跳槽的人,混不下去了”
☞起底 Windows 35 年发展史
☞赠书 | 新手指南——如何通过HuggingFace Transformer整合表格数据
☞想在边缘运行计算机视觉程序?先来迎接挑战!
☞《中国区块链发展报告(2020)》导读:全球区块链政策及监管重点趋势
点分享点点赞点在看

有安全研究者混入了 PHP 8.0 开发组!相关推荐

  1. ibm服务器的虚拟控制台,IBM Worklight 6.0 - 开发服务器/控制台问题

    在Worklight v5.0.6中预览Worklight Console的MBS中的混合应用程序非常简单,因为它的工作原理非常简单.IBM Worklight 6.0 - 开发服务器/控制台问题 W ...

  2. Struts 2创始人Patrick Lightbody看《精通Struts 2:Web 2.0开发实战 》

    <精通Struts 2:Web 2.0开发实战 > Apache Struts是目前最成功开源项目之一.除了一些基础性项目如Linux.MySQL以及若干编程语言外,很少有开源框架能像St ...

  3. From 《visual C++ 6.0开发工具与调试》

    From <visual C++ 6.0开发工具与调试> 1.          如何快速地规范代码缩进格式 选中所需要规范的代码,按shift+F8 2.          如何在Rel ...

  4. Windows Embedded CE 6.0开发初体验(二)CE开发环境 收藏

    上一篇<Windows Embedded CE 6.0开发初体验>之"嵌入式开发流程": http://blog.csdn.net/aawolf/archive/200 ...

  5. 用SignalR 2.0开发客服系统[系列3:实现点对点通讯]

    用SignalR 2.0开发客服系统[系列3:实现点对点通讯] 原文:用SignalR 2.0开发客服系统[系列3:实现点对点通讯] 前言 目录: 用SignalR 2.0开发客服系统[系列1:实现群 ...

  6. windows下qt5 kinect 2.0开发与环境配置

    上一次挑战杯我们用kinect1.8做了一个体感3d试衣系统,那时就想用qt来开发了,由于那时候很多东西都不懂,怎么也不知道怎样去配置环境,只能在vs2013中开发,结合qt开发界面.直到出了kine ...

  7. 实习小白::(转) Cocos2d-x 3.0开发(十三)使用CocoStudio编辑帧事件并关联到程序...

    1.概述 帧事件也是新加入的功能.这篇中我们将看到如何使用它.我们将上篇中制作的动画稍加修改.有图为证: 2.用途与原理 首先介绍一下帧事件.正如其名:一个与帧相关联的事件. 为什么要这么做呢?首先没 ...

  8. vc6.0开发环境两个辅助工具

    vc6.0开发环境两个辅助工具: 1)增加行号:http://download.csdn.net/detail/fjssharpsword/4162972 2)增加代码颜色和工具栏:http://do ...

  9. qt5.9.0调试如何查看变量的值_从0开发3D引擎(四):搭建测试环境

    大家好,本文介绍了3D引擎的测试方法,搭建了本地的测试环境. 上一篇博文 wonder-yyc:从0开发3D引擎(三):搭建开发环境​zhuanlan.zhihu.com 下一篇博文 wonder-y ...

最新文章

  1. Shift register(RAM-based)------ALTSHIFT_TAPS
  2. boost::set_union相关的测试程序
  3. eclipse在server中tomcat server找不到的问题
  4. win7系统出现蓝屏0x0000003b怎么解决
  5. eq值 推荐算法_利用 SVD 实现协同过滤推荐算法
  6. Twitter开放平台api key申请流程 (Twitter Consumer Key)
  7. 红外倒车雷达原理图_汽车里的毫米波雷达你知多少?
  8. linux函数 取值溢出,Linux eCryptfs工具parse_tag_3_packet()函数堆溢出漏洞
  9. 必应拼音输入法与搜狗拼音输入法体验性分析检测
  10. PreferenceActivity和PreferenceFragment对比
  11. 高校毕业设计管理系统【附源码】
  12. oracle执行计划之执行顺序(一看就会!)
  13. Linux系统安装(超级详细教程)
  14. 03 【Nginx虚拟主机和域名解析】
  15. 【OpenGL】室内3D弹球
  16. 一路狂奔的“幸运咖”,再造下一个“蜜雪冰城”?
  17. 小学五年级计算机课评课,小学数学五年级下册公开课《分数的意义》听课心得体会评课稿...
  18. 旅行商问题(Traveling Salesman Problem,TSP)的+Leapms线性规划模型及c++调用
  19. Shiro 第十七章 OAuth2集成
  20. ChatGPT技术原理 第七章:Seq2Seq模型

热门文章

  1. MD5之C#密码加密-备忘录
  2. python_day33- udp 多道技术
  3. HDU - 1757 A Simple Math Problem (矩阵快速幂)
  4. 【洛谷】【堆+贪心】P1484 种树
  5. HDU 1422 重温世界杯 (dp)
  6. ios高效开发-正确的使用枚举(Enum)
  7. click和blur冲突的问题
  8. DevExpress gridcontrol添加了复选框删除选中的多行/批量删除的方法
  9. CSS Expression用法总结
  10. [论文阅读] Annotation-Efficient Cell Counting