一.PHP的序列化和反序列化
(1).作用
PHP的序列化和反序列化是PHP中用于存储或传输PHP的值的一个过程。序列化是将变量转换为可存储或传输的字符串的过程,而反序列化则是将这些字符串转换回PHP变量的过程。这两个过程在PHP开发中非常有用,特别是在需要保存对象状态、进行深拷贝或在网络间传输复杂数据结构时。
(2).序列化实列
当序列化一个对象时,PHP会保存对象的类名、对象的所有属性(包括私有和受保护的属性),以及这些属性的值。但是,不会保存对象的方法(即类的函数)。这是因为PHP的序列化机制主要是用于保存对象的状态,而不是行为。
a.对象的序列化
class MyObject {
public $var1 = 'Hello World!';
protected $var2 = 'Protected var';
private $var3 = 'Private var';
function showVars() {
echo $this->var1 . "\n";
echo $this->var2 . "\n";
echo $this->var3 . "\n";
}
}
$obj = new MyObject();
$serialized = serialize($obj);
echo $serialized;
输出
O:8:"MyObject":3:{s:5:"var1";s:12:"Hello World!";s:8:"*var2";s:13:"Protected var";s:10:"MyObjectvar3";s:10:"Private var";}
这里,O
表示对象,8
是类名的长度,MyObject
是类名,3
是对象属性的数量。每个属性都表示为s
(字符串类型)后跟属性名、长度和值。对于受保护和私有属性,PHP在属性名前添加了前缀(*
对于受保护属性,以及类名和var
前缀对于私有属性)来确保它们在反序列化时能够正确地映射回原始对象。
b.键值数组(关联数组)的序列化
<?php
$array = array("a" => "apple", "b" => "banana", "c" => array("x", "y", "z"));
// 序列化数组
$serializedArray = serialize($array);
echo $serializedArray;
// 输出类似:a:3:{s:1:"a";s:5:"apple";s:1:"b";s:6:"banana";s:1:"c";a:3:{i:0;s:1:"x";i:1;s:1:"y";i:2;s:1:"z";}}
?>
(3).反序列化
反序列化是序列化的逆过程,即将序列化的字符串转换回PHP的原始数据结构。PHP提供了unserialize()
函数来实现这一功能。
$serializedData = 'O:8:"MyClass":1:{s:7:"myValue";s:5:"Hello";}';
$object = unserialize($serializedData);
// 假设 MyClass 有一个公共属性 myValue
echo $object->myValue; // 输出: Hello
在上面的例子中,$serializedData
是一个序列化的字符串,它表示一个名为 MyClass
的对象,该对象有一个名为 myValue
的公共属性,其值为 "Hello"
。通过 unserialize()
函数,我们能够将这个字符串转换回原始的 MyClass
对象。
二.PHP反序列化漏洞
(1).原理
PHP 的 serialize()
函数用于将 PHP 值(包括数组和对象)转换成可存储或传输的字符串。相反,unserialize()
函数则用于将这些字符串转换回原始的 PHP 值。然而,如果 unserialize()
被用于处理恶意构造的序列化字符串,它可能会实例化未预期的类、调用未授权的方法或执行其他危险操作。
攻击者可以通过构造一个特制的序列化字符串,该字符串在反序列化时会触发某些有害的行为,如执行任意代码、修改应用程序的状态或泄露敏感信息。
(2).pikachu靶场练习
我们可以尝试反序列化一个XSS攻击
O:1:"S":1:{s:4:"test";s:25:"<script>alert(1)</script>";}
O:1:"S":1:{s:4:"test";s:39:"<script>alert(document.cookie)</script>";}//弹出cookie
(3).防御
- 输入验证:
- 对所有输入数据进行严格的验证,确保它们符合预期的格式和类型。在反序列化之前,检查输入数据是否确实是有效的序列化字符串。
- 使用正则表达式或其他验证方法来检查序列化字符串的格式是否正确,但请注意,这种方法可能无法完全防止所有类型的攻击,因为序列化字符串的格式相对复杂且可以构造出绕过简单验证的恶意数据。
- 使用安全的替代方案:
- 如果可能,避免使用
unserialize()
函数,而是寻找更安全的替代方案来存储和传输数据。例如,可以使用JSON、XML或其他格式来编码数据,并使用相应的解析函数来处理它们。 - 对于需要存储复杂数据结构的情况,可以考虑使用数据库或其他数据存储系统来管理这些数据,而不是将它们序列化为字符串。
- 如果可能,避免使用
- 限制反序列化的使用:
- 仅在绝对必要时才使用
unserialize()
函数,并尽量减少其使用范围。避免在不受信任的数据上调用unserialize()
。 - 如果必须使用
unserialize()
,请确保在受控的环境中执行它,并限制可以反序列化的类。可以使用allowed_classes
选项(在PHP 7.0.0及以上版本中可用)来指定允许反序列化的类。
- 仅在绝对必要时才使用
- 更新和修补:
- 定期更新PHP及其相关组件到最新版本,以获取最新的安全补丁和修复。
- 监控与PHP反序列化漏洞相关的安全公告和漏洞报告,以便在发现新漏洞时及时采取行动。
- 使用自定义的反序列化函数:
- 如果需要更细粒度的控制,可以编写自定义的反序列化函数来替代
unserialize()
。在自定义函数中,可以添加额外的验证和清理步骤来确保数据的安全性。
- 如果需要更细粒度的控制,可以编写自定义的反序列化函数来替代
- 日志和监控:
- 对所有反序列化操作进行日志记录,以便在发生异常时能够追踪和调查。
- 监控应用程序的性能和异常行为,以便及时发现和响应潜在的安全威胁。
- 安全编码实践:
- 遵循最佳的安全编码实践,如最小权限原则、错误处理和日志记录等。
- 对开发人员进行安全培训,提高他们对安全漏洞的认识和防范能力。
请注意,这些防御措施并不是孤立的,而是应该相互结合使用,以形成一个全面的安全防御体系。此外,随着PHP及其相关组件的不断发展,新的安全漏洞和攻击技术可能会出现,因此持续关注和更新安全策略是非常重要的。