php反序列化_network

面向对象编程(OOP)

它是一种编程范式,使用"对象"的概念来组织和数据。在PHP中,OOP提供了一种更结构化、模块化的方式来开发应用程序。

OOP的基本特征

  1. 封装(Encapsulation)

    将数据和操作数据的方法绑定在一起

    通过访问修饰符(public, protected, private)控制可见性

  2. 继承(Inheritance)
    子类可以继承父类的属性和方法
    实现代码复用和层次关系

  3. 多态(Polymorphism)
    同一操作作用于不同类的实例会产生不同行为
    通过接口和抽象类实现

  4. 抽象(Abstraction)
    提取关键特征而忽略非必要细节
    通过抽象类和接口实现

OOP的重要组成部分

  1. 类(Class)

    对象的蓝图或模板:

    class Animal { /* ... */ }
    
  2. 对象(Object)

    类的具体实例:

    "new"关键字用于创建对象

    $dog = new Animal();//$dog是Animal类的一个对象 
    
  3. 属性(Property)

    类/对象的特征或数据(也称为成员变量):

    public $name; 
    private $age; 
    protected $gender; 
    
    • public修饰的属性可以从类外部直接访问

    • private修饰的属性只能在类内部访问

    • protected修饰的属性可以在类内部、同一包中其他类以及不同包中的子类中访问

    访问控制修饰符的不同,序列化后属性的长度和属性值会有所不同:

    • public:属性被序列化的时候属性值会变成属性名
    • protected:属性被序列化的时候属性值会变成\x00*\x00属性名
    • private:属性被序列化的时候属性值会变成\x00类名\x00属性名
    • 其中:\x00表示空字符,但是还是占用一个字符位置
  4. 方法(Method)

    类/对象的行为或功能(也称为成员函数):

    用于定义对象能执行的操作

    public function eat() { /* ... */ }
    

OOP实例

class Animal {
    // 属性
    public $name;
    public $type;

    // 构造函数
    public function __construct($name, $type) {
        $this->name = $name;
        $this->type = $type;
    }

    // 方法
    public function makeSound() {
        echo "This animal makes a sound.";
    }
}

// 创建对象
$dog = new Animal("Buddy", "Dog");
$cat = new Animal("Whiskers", "Cat");

// 调用对象的方法
$dog->makeSound(); // 输出: This animal makes a sound.
$cat->makeSound(); // 输出: This animal makes a sound.

class User{  //定义了名为User的类
    //属性
    protected $name;
    //方法
    public function say(){
        return $this->name .'说,你好';
    }
    //方法
    public function setName(string $name){
    $this->name = $name;
    }
}

//创建对象
$obj = new User;
//调用对象的方法
$obj->setName('zzz'); //输出:zzz
echo $obj->say(); //输出:说,你好

序列化与反序列化

在PHP中,序列化和反序列化是以一种将变量(如数组或对象)转换成可以存储或传输的字符串格式的方法,再将这些字符串还原为原始的变量。一般用于处理数据持久化、会话存储或跨脚本通信。

变量与数据结构

变量:所有数据的存储单元都以变量的形式存在

数据结构:是指组织和存储数据的方式

在PHP中,变量和数据等结构,构成了程序数据存储和处理的基础体系。它们之间存在着层级递进的关系:

变量 → [基本数据类型] → [复合数据类型] → [高级数据结构]
      (标量类型)      (数组/对象)      (Spl数据结构/自定义结构)

PHP序列化

序列化是将PHP的数据结构(如数组或对象),转化成一个串行的字节流(即字符串),从而保存到文件或数据库中,以及实现网络传输。

通过serialize()函数来执行序列化:

// 序列化
$data = ['name' => 'John', 'age' => 30, 'is_admin' => false];
$serialized = serialize($data);
echo $serialized; 
// 输出类似:
//a:3:{s:4:"name";s:4:"John";s:3:"age";i:30;s:7:"is_admin";b:0;}
  • a:表示array(数组)
  • 3:数组有三个元素
  • s:string(字符串)
  • i:integer(整数索引)
  • b:布尔值(1为true)
  • 4/7:字符串长度

PHP反序列化

PHP反序列化主要用于数据恢复、网络通信(在网络上传输的对象或数组数据,通过序列化转换为字符串后存储或传输,接收端需要通过反序列化将其恢复为原始格式)、会话管理及数据持久化等场景,通常用unserialize()函数来执行。

  • 反序列化中,字符串必须用双引号表示,而不能使用单引号包裹

PHP反序列化漏洞

原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。

在反序列化的过程中自动触发了某些魔术方法。

漏洞触发条件: unserialize()函数的参数、变量可控,php文件中存在可利用的类,类中有魔术方法

魔术方法

魔术方法是PHP中一类特殊的方法,它们以双下划线(__)开头。这些方法会在特定情况下被PHP自动调用,用于实现对象的特殊行为或提供额外功能。

__construct()

类的构造函数,在创造对象时自动调用(触发时机)

省去了$a->action(); // 调用方法

构造函数consturct() 进行了自动调用

__destruct()

析构函数,用于在对象销毁时执行一些清理操作。

触发时机:

  1. 脚本执行完毕,所有未被销毁的对象会被自动销毁
  2. 手动销毁对象,使用unset()显式销毁对象

__sleep()

序列化函数serialize()会在执行前检查类中是否有___sleep()魔术方法,如果存在会先调用该方法,在执行序列化操作。

功能:对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性

参数:成员属性(可选)

返回值:需要序列化存储的成员属性(可选)

<?php
class Person
{
    public $sex;
    public $name;
    public $age;

    public function __sleep()
    {
        // 返回需要序列化的属性
        return array('name', 'age');
    }
}

$person = new Person();
$person->sex = 'male';
$person->name = '张三';
$person->age = 25;

$serialized = serialize($person);
echo $serialized;
?>

在这个示例中,__sleep() 方法返回了 $name$age 属性,因此只有这两个属性会被序列化,而 $sex 属性不会被序列化。

__wakeup()

反序列化函数unserialize()会在执行前检查,是否存在__wakeup()方法,如果存在,则先调用该方法。

  • __ wakeup()在反序列化触发之前,__destruct()在反序列化触发之后

该方法常用于反序列化中重新建立数据库的连接或执行其他初始化操作,也就是说,可以在此方法中重新初始化对象。

__toString()

表达方式的错误会调用,把对象当成字符串调用(eg.使用print或echo输出对象,或者在字符串环境中使用对象,例如字符串拼接、格式化字符串等。)

<?php
class Person {
    private $name;
    private $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }

    public function __toString() {
        return "Name: " . $this->name . ", Age: " . $this->age;
    }
}

$person = new Person("张三", 25);
echo $person; // 输出: Name: 张三, Age: 25
?>

__invoke()

格式表达错误会调用,把对象当成函数调用

<?php
class OOO{
    var $lalala="hahaha";
    public function __invoke(){
        echo "这不是函数!";
    }
}
$test=new OOO();
echo $test->lalala;

echo $test()->lalala;
?>

__call()

当调用不存在的方法时会触发

参数:存在两个参数传参

返回值:调用的不存在的方法的名称和参数

<?php
class qwe{
    public function __call($arg1,$arg2){
        echo "$arg1,$arg2[0]";
    }
}
$test=new qwe();
$test->callxxx('a');
?>

调用的方法callxxx()不存在,触发魔术方法call()

触发call(),传参$arg1,$arg2 (即callxxx,a)

$arg1,调用的不存在的方法的名称; Sarg2,调用的不存在的方法的参数;

__callStatic()

静态调用或调用成员常量时使用的方法不存在

和__call()差不多 作用一样 调用方法不同

<?php
class ABC{
    public function __callStatic($arg1,$arg2){
        echo "$arg1,$arg2[0]";
    }
}
$test=new ABC();
$test::callxxx('a');
?>

__get()

触发时机:调用的成员属性不存在

参数:传参$arg1

返回值:不存在的成员属性的名称

<?php
class User{
     public $var1;
     public function __get($arg1){
           echo $arg1;
     }
}
$test=new User();
$test->var2;
?>

调用的成员属性var2不存在
触发__get(),把不存在的属性名称var2赋值给$arg1

__set()

触发时机:给不存在的成员属性赋值

参数:传参$arg ,$arg2

返回值:不存在的成员属性的名称和赋的值

<?php
class doll{
    public $var1;
    public function __set($arg1,$arg2){
        echo $arg1.",".$arg2;
    }
}
$test=new doll();
$test->var2=1;
?>

调用的成员属性var2不存在

触发__set(),把不存在的属性名字var2赋值给$arg1

var2赋的值赋值给$arg2

__isset()

触发时机:对不可访问或不存在的属性使用isset()empty()时,__isset()会被调用

参数:传参$arg1

返回值:不存在的成员属性的名称

<?php
class hhuu{
    private $ooo;
    public function __isset($r){
        echo $r."不可访问!";
    }
}
$p=new hhuu();
isset($p->ooo);
?>
  • private关键字修饰的成员只能在定义它的类内部访问,外部代码无法直接访问或修改这些成员。

__unset()

触发时机:对不可访问属性使用unset()

参数:传参$arg1

返回值:不存在的成员属性的名称

<?php
class aaa{
    private $var;
    public function __unset($arg1){
        echo $arg1;
    }
}
$lol = new aaa();
unset($lol->var);
?>

__isset()类似

__clone()

当使用 clone 关键字复制一个对象时自动调用。

<?php
class r {
    private $var;
    public function __clone(){
        echo "__clone obj";
    }
}
$obj = new r();
$vbvb=clone $obj;
?>

__set _state()

调用 var_export() 函数导出类代码时,此静态方法会被自动调用。

参数:一个数组,包含对象的所有属性及其对应的值。

其中包含按 ['property' => value, ...] 格式排列的类属性。

返回对象:必须返回一个对象,通常是该类的一个实例。

<?php

class A
{
    public $var1;
    public $var2;

    public static function __set_state($an_array)
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

$b = var_export($a, true);
var_dump($b);
eval('$c = ' . $b . ';');
var_dump($c);
var_dump($b);

?>

__autoload()

当程序实例化一个未定义的类时,自动调用该方法来加载相应的类文件。

自动动调用 __autoload() 方法,并将类名作为字符串参数传递给它,这样可以避免手动包含每个类文件,简化代码管理。

// 定义 __autoload() 方法
function __autoload($_className) {
    require $_className . '.class.php';
}

// 实例化一个类
$demo = new Computer();

尝试实例化 Computer 类时,如果该类尚未被定义,PHP 会自动调用 __autoload() 方法,并将类名 Computer 作为参数传递给它。

__autoload() 方法会根据类名 Computer 自动包含 Computer.class.php 文件。

__debugInfo()

当通过 var_dump()转储对象,获取应该要显示的属性的时候, 该函数就会被调用。

如果对象中没有定义该方法,那么将会展示所有的公有、受保护和私有的属性。

<?php
class C {
    private $prop;

    public function __construct($val) {
        $this->prop = $val;
    }

    public function __debugInfo() {
        return [
            'propSquared' => $this->prop ,
        ];
    }
}

var_dump(new C(42));
?>

从序列化到反序列化这几个函数的执行过程是:
__construct() ->__sleep() -> __wakeup() -> __toString() -> __destruct()

posted @ 2025-04-26 19:38  llz233  阅读(37)  评论(0)    收藏  举报