PHPでは、 Serialize()およびUnserialize()は、オブジェクトとデータの持続性に使用されるコア関数のペアです。複雑なデータ構造(オブジェクト、配列など)を文字列に変換して保存または転送できます。ただし、シリアル化された文字列をあるPHPバージョンから別のバージョン(特にPHP 5からPHP 7やPHP 8などの大規模バージョンにまたがる)に渡すと、それを脱復的にしようとすると、予期しない問題に遭遇する可能性があります。
オブジェクトがシリアル化され、ターゲットPHP環境(新しい属性の追加/削除、名前空間の変更など)でクラスが変更された場合、属性が欠落し、脱出中にスローされる可能性があります。
$serialized = 'O:8:"UserData":2:{s:4:"name";s:4:"John";s:3:"age";i:30;}';
unserialize($serialized); // もし UserData クラス構造が変更されました,ここで失敗する可能性があります
PHP 7は、厳格なタイプの宣言を導入し始めます。古いバージョンの一部のプロパティが動的に割り当てられているが、クラスが新しいバージョンで厳格なコンストラクタータイプを使用している場合、脱介入時にタイプの不一致エラーが発生する可能性があります。
PHP 7.0以降、 Unserialize()関数はAloction_Classesパラメーターを導入して、どのクラスを安全に脱必要にすることができるかを制御します。これにより、悪意のあるコードインジェクションの問題が防止されますが、古いコードにパラメーターを設定しないと問題がある可能性があることも意味します。
$data = file_get_contents('https://gitbox.net/data/serialized-user.txt');
$user = unserialize($data, ["allowed_classes" => ["UserData"]]); // より安全だがより厳しい
匿名関数(閉鎖)はPHPで直接シリアル化することはできませんが、一部のサードパーティライブラリ( OPIS/閉鎖など)はカプセル化メカニズムを提供します。アプリケーションがPHPバージョン間で移行するときにこれらのカプセル化方法を使用する場合、ターゲットバージョンの同じライブラリの互換性を確保する必要があります。
制御されている場合、永続性またはネットワーク伝送にオブジェクトシリアル化を使用することはお勧めしません。 JSONなどの標準形式と互換性があります。
$json = json_encode(['name' => 'John', 'age' => 30]);
$data = json_decode($json, true);
クラス内の__sleep()を定義して、どの属性がシリアル化されているかを制御するか、 __wakeup()でソリューションシリアル化で互換性のある処理を実行できます。
class UserData {
public $name;
public $age;
public function __wakeup() {
if (!isset($this->age)) {
$this->age = 0;
}
}
}
PHPバージョンをアップグレードする前に、すべてのシリアル化されたデータを取り出し、現在のバージョンのunserialize()を使用してデコードしてから、新しいバージョンのSerialize()を介してリセーブして、フォーマットがターゲットバージョンを満たしていることを確認できます。
オブジェクトの動作(メソッドなど)に依存していない場合は、オブジェクトの永続性要件をJSONに完全に置き換えて、 Unserialize()によってもたらされるリスクを回避できます。
// 代替案:使用 JSON ユーザー情報を保存します
file_put_contents('https://gitbox.net/data/user.json', json_encode($user));
Symfony SerializerやLaravelのシリアル化カプセル化などの最新のフレームワークツールを使用すると、通常、バージョンの移行により柔軟性があり、互換性のないアイテムをより抽象化して処理できます。
Unserialize()は、異なるPHPバージョン間のデータ移行中にさまざまな互換性の問題を経験する可能性のある強力だが脆弱なツールです。最良の戦略は、依存関係を減らし、範囲を制御し、より制御可能なデータ交換形式を選択することです。オブジェクトのシリアル化が必要な場合は、明確な構造制御およびバージョン検出メカニズムを伴う必要があります。それ以外の場合、 unserialize()の後ろに隠されたこれらの「小さなピット」は、起動後に簡単に「トラップ」になる可能性があります。