目录
一、基本概念
1. 序列化
2. 反序列化
二、反序列化漏洞
1. 漏洞产生原因
2. 魔术方法
3.利用魔术方法进行攻击的示例:
一、基本概念
什么是 PHP 反序列化
- PHP 反序列化是将序列化后的字符串恢复为原始 PHP 数据类型(如对象、数组等)的过程。序列化是把数据结构转化为可存储或传输的字符串格式,而反序列化则是其逆操作,将这个字符串重新转换回原来的数据结构,以便在程序中继续使用。
1. 序列化-serialize()
函数
序列化在 PHP 中是一种将复杂数据结构转换为字符串以便存储或传输的重要机制。它使得可以轻松地保存和恢复对象、数组等数据类型。
通过使用 serialize()
函数实现、例如:
$arr = array('key1' => 'value1', 'key2' => 'value2');
$serializedData = serialize($arr);
echo $serializedData;
// 输出:a:2:{s:4:"key1";s:6:"value1";s:4:"key2";s:6:"value2";}
-
序列化的过程:
- php中的
serialize()
函数负责将给定的数据结构转换为序列化字符串。例如,对于一个包含多个键值对的关联数组,序列化后的字符串会清晰地展示每个键值对的表示形式。序列化的格式通常以特定的字符开头,后面跟着数据结构的描述信息。例如,对于数组,通常以a:
开头,接着是数组元素的数量,然后是一系列键值对的描述。如一个简单的数组$arr = array('name' => 'John', 'age' => 30);
,序列化后可能是a:2:{s:4:"name";s:4:"John";s:3:"age";i:30;}
。其中,a:2
表示这是一个包含两个元素的数组,s:4:"name";s:4:"John"
表示键为长度为 4 的字符串name
,对应的值为长度为 4 的字符串John
,s:3:"age";i:30
表示键为长度为 3 的字符串age
,对应的值为整数 30。 - 对于对象的序列化,会包含对象的类名以及各个属性的信息。如果对象的类中定义了魔术方法,这些方法也会在序列化过程中被考虑进去,但通常不会直接体现在序列化后的字符串中,而是在反序列化时根据特定的规则触发相应的魔术方法执行。
- php中的
-
序列化的用途:
- 数据存储:在需要将数据保存到文件、数据库或其他持久存储介质时,序列化可以将复杂的数据结构转换为易于存储的字符串形式。例如,在一个用户信息管理系统中,可以将用户对象序列化后存储到数据库中,以便在需要时恢复用户的状态。
- 网络传输:当需要在不同的系统或组件之间传输数据时,序列化可以确保数据以一种通用的格式进行传输。例如,在一个分布式应用中,客户端可以将请求数据序列化后发送给服务器,服务器再将接收到的序列化数据反序列化以处理请求。
2. 反序列化-unserialize()
函数
反序列化则是将序列化后的字符串还原为原始数据结构的操作,使用 unserialize()
函数。例如:
$serializedData = 'a:2:{s:4:"key1";s:6:"value1";s:4:"key2";s:6:"value2";}';
$originalArr = unserialize($serializedData);
print_r($originalArr);
// 输出:Array ( [key1] => value1 [key2] => value2 )
-
反序列化的过程:
- php 中的
unserialize()
函数用于执行反序列化操作。它接受一个序列化后的字符串作为参数,并尝试将其转换回原始的数据结构。在反序列化过程中,PHP 解析器会根据序列化字符串的格式,逐步还原出数据结构的各个部分。对于数组,会根据字符串中的键值对信息重新构建数组。对于对象,会根据类名创建对象,并恢复对象的属性值。如果序列化字符串中包含对象的引用,反序列化过程也会正确处理这些引用,确保恢复后的对象之间的关系与序列化前一致。 - 例如,对于前面序列化得到的字符串
a:2:{s:4:"name";s:4:"John";s:3:"age";i:30;}
,使用unserialize()
函数后,将得到原始的数组array('name' => 'John', 'age' => 30)
。
- php 中的
-
反序列化的用途:
- 数据恢复:从数据库或文件中读取先前存储的序列化数据,并将其反序列化以恢复数据的原始状态。例如,在一个电子商务网站中,用户的购物车信息可以在用户登录时从数据库中读取并反序列化,以便继续上次的购物流程。
- 网络通信:在接收来自其他系统的序列化数据时,进行反序列化以获取原始的数据结构,以便进行进一步的处理。例如,在一个微服务架构中,服务之间的通信可能会涉及到序列化和反序列化数据。
二、反序列化漏洞
1. 漏洞产生原因
PHP 反序列化漏洞主要是由于应用程序在处理用户输入的序列化数据时没有进行充分的验证和过滤,导致攻击者可以构造恶意的序列化数据来执行任意代码或进行其他恶意操作。
-
用户输入的不可信性:
- 在很多应用程序中,用户可以通过各种方式提供数据,这些数据可能会被序列化和反序列化。例如,用户提交的表单数据、从外部 API 接收的数据或者从文件中读取的数据。如果应用程序没有对这些数据进行严格的验证,攻击者就可以构造恶意的序列化数据并将其提交给应用程序进行反序列化。
- 例如,一个社交网络应用允许用户上传自定义的头像图片,并将图片的元数据(如尺寸、格式等)序列化后存储在数据库中。如果攻击者能够篡改上传的图片元数据,使其包含恶意的序列化数据,那么当应用程序从数据库中读取并反序列化这些数据时,就可能触发漏洞。
-
危险的反序列化操作:
- 当应用程序在反序列化过程中执行了一些敏感操作时,就可能存在安全风险。例如,如果一个类的魔术方法(如
__wakeup()
、__destruct()
等)中包含了危险的操作,如执行系统命令、访问敏感文件或进行数据库操作,而应用程序又对用户输入的序列化数据进行反序列化,那么攻击者就可以构造恶意的序列化数据来触发这些危险操作。 - 以
__destruct()
方法为例,这个方法在对象被销毁时自动调用。如果一个类的__destruct()
方法中包含了执行系统命令的代码,攻击者可以构造一个包含该类对象的序列化字符串,并通过某种方式让应用程序反序列化这个字符串。当应用程序销毁这个对象时,就会执行__destruct()
方法中的恶意代码。
- 当应用程序在反序列化过程中执行了一些敏感操作时,就可能存在安全风险。例如,如果一个类的魔术方法(如
2. 魔术方法
在 PHP 中,魔术方法是一些以双下划线开头和结尾的特殊方法,它们在特定的情况下会自动被调用。在反序列化过程中,一些魔术方法可能会被恶意利用来执行攻击代码。
-
常见的魔术方法及其作用:
__construct()
:在对象创建时调用。通常用于初始化对象的属性。在反序列化过程中,如果一个类定义了__construct()
方法,并且反序列化的对象是该类的实例,那么在反序列化完成后,会自动调用这个方法。攻击者可以通过构造恶意的序列化数据,使得在反序列化过程中创建的对象的__construct()
方法执行恶意代码。__destruct()
:在对象销毁时调用。这个方法可以用于释放资源或执行一些清理操作。如果一个类的__destruct()
方法中包含了危险的操作,如执行系统命令或访问敏感文件,攻击者可以通过反序列化包含该类对象的序列化数据,然后等待对象被销毁时触发__destruct()
方法中的恶意代码。__wakeup()
:在使用unserialize()
时自动调用。这个方法通常用于在反序列化过程中重新初始化对象的属性或执行一些其他的操作。攻击者可以在类的__wakeup()
方法中插入恶意代码,当应用程序反序列化包含该类对象的序列化数据时,就会执行__wakeup()
方法中的恶意代码。__sleep()
:在使用serialize()
时调用。这个方法可以用于指定在序列化过程中需要保存哪些属性,或者进行一些其他的准备工作。攻击者可以通过分析类的__sleep()
方法,了解哪些属性会被序列化,然后构造恶意的序列化数据来修改这些属性的值,从而在反序列化后执行恶意操作。
-
3.利用魔术方法进行攻击的示例:
- 以下是一个利用
__destruct()
方法进行攻击的示例代码:
- 以下是一个利用
class EvilClass {public function __destruct() {// 恶意代码,比如执行系统命令system('rm -rf /'); }
}$evilObj = new EvilClass();
$serializedEvil = serialize($evilObj);
// 假设这里攻击者可以控制传入 unserialize 的数据
unserialize($serializedEvil);
在这个例子中,EvilClass
类的__destruct()
方法中包含了执行危险系统命令的代码。如果攻击者能够将序列化后的$evilObj
对象传递给应用程序进行反序列化,那么当对象被销毁时,就会执行__destruct()
方法中的恶意代码,删除系统中的所有文件。