目次
PHPのシリアル化とは何ですか?
使用される2つの関数
serialize() //函数用于序列化对象或数组,并返回一个字符串。
unserialize() //函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
serialize()インスタンス
配列シリアル化インスタンス
配列のセットをシリアル化した後、文字列の文字列を取得します
<?php
highlight_file(__FILE__);
$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
$s = serialize($a);
echo $s;
//a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
?>
オブジェクトシリアル化インスタンス
オブジェクトをシリアル化した後、文字列の文字列を取得します
<?php
highlight_file(__FILE__);
class test_class{ //创建类
var $test = '123'; //在类中新建一个test变量
}
$class1 = new test_class; //创建对象
$class_str = serialize($class1);//序列化对象
echo $class_str;//输出序列化后的字符串
//O:10:"test_class":1:{s:4:"test";s:3:"123";}
?>
unserialize()インスタンス
配列逆シリアル化インスタンス
シリアル化された文字列を標準形式で逆シリアル化すると、次の配列が得られます
<?php
highlight_file(__FILE__);
$str = 'a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";} ';
$un_str=unserialize($str);
var_dump($un_str);
?>
オブジェクトの逆シリアル化インスタンス
シリアル化された文字列を標準形式で逆シリアル化して、新しいオブジェクトを取得します
<?php
highlight_file(__FILE__);
$str = 'O:10:"test_class":1:{s:4:"test";s:3:"123";}';
$un_str=unserialize($str);
var_dump($un_str);
?>
シリアル化された文字列形式の分析
O:10:"test_class":1:{s:4:"test" ; s:3:"123" ;}
オブジェクトタイプ:クラス名の長さ:クラス名:クラス内の変数の数:{変数:変数名の長さ:変数名;変数タイプ:変数の内容の長さ:変数の内容}
a:3:{s:1:"a" ; s:5:"Apple" ; s:1: "b"; s:6: "バナナ"; s:1: "c"; s:7: "ココナッツ";}
配列タイプ:配列の数:{変数:変数名の長さ:変数名;変数タイプ:変数の内容の長さ:変数の内容; ...}
- シリアル化には2つのタイプがあります。
- オブジェクトタイプ
- 配列型
- 中括弧内では、2つのセミコロン(;)がグループを形成し、2つのセミコロンが変数の内容を表していることがわかります。
- {s:4:"テスト" ; s:3:"123" ;}
可変型
異なるストレージタイプは、シリアル化後に異なる文字に変換されます
a - array b - boolean
d - double i - integer
o - common object r - reference
s - string C - custom object
O - class N - null
R - pointer reference U - unicode string
シリアル化後のパブリック、プライベート、および保護の違い
- パブリック(パブリック):このクラス、外部クラス、およびサブクラス内でアクセスできます
- プライベート(プライベート):このクラス内でのみ使用可能
- 保護(保護):このクラスまたはサブクラスまたは親のみがアクセスできます
上記の3つの修飾子に従ってシリアル化すると、次の結果が得られます。
O:10: "test_class":3:{
s:6: "test_1"; s:3: "str";
s:18: "test_classtest_3"; i:123;
s:9: "* test_2"; i:123;
}
- public:基本的にシリアル化後の変更はありません
- private:シリアル化後、クラスの名前が変数名に追加され、変数名の長さも長くなります
- 保護:シリアル化後、変数名の前に余分な*がありますが、変数名の長さが9バイトになるのはなぜですか?
- 実際、保護された属性がシリアル化される場合、形式は%00 *%00メンバー名です。
魔法の方法
PHPの逆シリアル化を使用する場合、逆シリアル化でマジックメソッドを使用して、メソッドに機密性の高い操作があるかどうかを確認する必要があることがよくあります。
PHPは、__(2つのアンダースコア)で始まるすべてのクラスメソッドをマジックメソッドとして予約しています。
一般的な魔法の方法
__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__sleep() //此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
__wakeup() //经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
メソッド呼び出しの例
__睡眠()
serialize()関数は、クラスにマジックメソッド__sleep()があるかどうかをチェックします。存在する場合は、シリアル化操作が実行される前に、メソッドが最初に呼び出されます。この関数を使用して、オブジェクトをクリーンアップし、シリアル化する必要があるオブジェクト内のすべての変数の名前を含む配列を返すことができます。メソッドが何も返さない場合、NULLがシリアル化され、E_NOTICEレベルのエラーが生成されます。
__目を覚ます()
unserialize()は、__ wakeup()メソッドがあるかどうかを確認します。存在する場合は、最初に__wakeupメソッドが呼び出され、オブジェクトに必要なリソースが事前に準備されます。
__toString()
__toString()メソッドは、クラスが文字列として扱われるときに応答するために使用されます。たとえば、echo $ obj;何を表示するか。このメソッドは文字列を返す必要があります。そうしないと、E_RECOVERABLE_ERRORレベルの致命的なエラーが発行されます。
デシリアライズの脆弱性の原則
逆シリアル化unserialize()のパラメーターが制御可能な場合、シリアル化された文字列を作成して、オブジェクトの内部変数や関数を変更できます。
場合:
<?php
error_reporting(0);
show_source(__FILE__);
class c{
public $str = 111;
function __wakeup(){
echo $this->str;
}
}
$str = $_GET['str'];
if (isset($str)){
unserialize($str);
}
?>
デシリアライズを制御できるようになると、クラス内の変数の内容を変更できるようになります
poc
?str=O:1:"c":1:{s:3:"str";s:3:"123";}
もちろん、それほど単純ではありません。値の内容を変更するだけで、xssと入力することもできます。
poc
?str=O:1:"c":1:{s:3:"str";s:24:"<script>alert()</script>";}
もちろん、これは単純なケースです。次回は、phpデシリアライズの高度な知識について説明します。