السلسلة Serialization في PHP: المفهوم، الاستخدامات، الأداء والمخاطر الأمنية
مقدمة
تُعد لغة PHP من أشهر لغات البرمجة المستخدمة في تطوير تطبيقات الويب، وذلك بفضل بساطتها وقوة أدواتها المدمجة. ومن بين هذه الأدوات المهمة ميزة السلسلة (Serialization)، والتي تُستخدم لتحويل الكائنات والبيانات المعقدة إلى تمثيل يمكن نقله أو تخزينه بسهولة، ثم استعادته لاحقاً دون فقدان بنيته الأصلية. هذه التقنية تُستخدم بشكل واسع في التطبيقات التي تتطلب نقل البيانات بين الجلسات (Sessions)، أو تخزينها في قواعد البيانات أو ملفات، أو تمريرها عبر الشبكة.
في هذا المقال المطوّل، سيتم تناول موضوع Serialization في PHP بعمق، بدءًا من تعريفه، ومرورًا بكيفية استخدامه، وفهم بنيته التقنية، ووصولاً إلى تحليل دقيق لمخاطره الأمنية وأفضل الممارسات التي يجب اتباعها لتجنب الثغرات.
تعريف Serialization في PHP
في علم الحوسبة، Serialization هي عملية تحويل كائن object أو بنية بيانات معقدة مثل arrays إلى سلسلة من المحارف (string) يمكن حفظها أو نقلها بسهولة، بينما تشير العملية العكسية، unserialization، إلى إعادة تحويل هذه السلسلة إلى بنيتها الأصلية.
في PHP، serialization تُنفذ باستخدام الدالة serialize()، أما unserialization فتُنفذ باستخدام unserialize().
php$person = array("name" => "Ali", "age" => 30);
$serialized = serialize($person);
// string output: a:2:{s:4:"name";s:3:"Ali";s:3:"age";i:30;}
$unserialized = unserialize($serialized);
آلية العمل الداخلية
عند استدعاء serialize() على مصفوفة أو كائن، تقوم PHP بترميز البيانات إلى سلسلة تحتوي على معلومات دقيقة عن كل عنصر: نوعه، قيمته، طوله، وترتيبه. هذا التمثيل يتبع تنسيقًا خاصًا تديره محركات PHP.
على سبيل المثال:
php$data = array("foo" => "bar", "baz" => 42);
echo serialize($data);
// output: a:2:{s:3:"foo";s:3:"bar";s:3:"baz";i:42;}
تحليل السلسلة:
-
a:2: تعني مصفوفة (array) تحتوي على عنصرين. -
s:3:"foo": المفتاحfooهو سلسلة من 3 حروف. -
s:3:"bar": القيمة المقابلة لـfoo. -
s:3:"baz"وi:42: مفتاح آخر وقيمته عدد صحيح.
Serialization للكائنات (Objects)
عند تطبيق serialize() على كائن، يتم تضمين اسم الصنف class وخصائصه العامة. لكن الخصائص الخاصة (private) والمحمية (protected) يتم ترميزها بطريقة تحفظ حدود الرؤية (visibility).
phpclass User {
public $name;
private $password;
public function __construct($name, $password) {
$this->name = $name;
$this->password = $password;
}
}
$user = new User("Omar", "1234");
$serialized = serialize($user);
الناتج يكون كالتالي:
cssO:4:"User":2:{s:4:"name";s:4:"Omar";s:13:"Userpassword";s:4:"1234";}
يشير O:4:"User" إلى كائن من صنف “User” يحتوي على خاصيتين. لاحظ كيف يتم ترميز الخاصية الخاصة password بتنسيق Userpassword، ما يدل على أنها محمية من الوصول المباشر.
__sleep() و __wakeup(): التحكم في عملية Serialization
توفر PHP دوال سحرية للتحكم في ما يحدث عند تسلسل الكائنات:
__sleep()
تُستخدم عند استخدام serialize() على كائن. يُمكنك تحديد أسماء الخصائص التي ينبغي تسلسلها.
phpclass Config {
public $db;
private $cache;
public function __sleep() {
return ['db']; // فقط خاصية db سيتم تسلسلها
}
}
__wakeup()
تُستخدم عند استدعاء unserialize() لإعادة تهيئة الكائن بعد استعادته من السلسلة.
phpclass Config {
public function __wakeup() {
// إعادة الاتصال بقاعدة البيانات أو إعادة تحميل الموارد
}
}
استخدامات عملية للـ Serialization في PHP
1. الجلسات (Sessions)
تستخدم PHP serialization لتخزين بيانات الجلسة في الملفات:
php$_SESSION['user'] = $userObject;
عند كتابة الجلسة على القرص، يتم serializing للكائن تلقائيًا.
2. قواعد البيانات
غالبًا ما نحتاج لتخزين كائن أو مصفوفة معقدة في عمود من نوع TEXT:
php$settings = serialize($configArray);
mysqli_query($conn, "INSERT INTO options (data) VALUES ('$settings')");
3. التخزين المؤقت (Caching)
تستخدم تقنيات مثل APCu أو Memcached serialization لتخزين البيانات المركبة:
phpapcu_store('data', serialize($largeArray));
مقارنة بين serialize() و json_encode()
| المعيار | serialize() |
json_encode() |
|---|---|---|
| قابلية القراءة | أقل | أكثر |
| دعم الكائنات | نعم | محدود |
| قابلية التبادل | لا (خاص بـ PHP) | نعم (صيغة JSON) |
| الأداء | أسرع للكائنات | أسرع للمصفوفات |
| الأمان | أكثر عرضة للهجمات | أكثر أمانًا |
ملاحظة: يُفضل استخدام json_encode() عند نقل البيانات إلى واجهات JavaScript أو API لأنها أكثر توافقًا وتقرأ بسهولة عبر اللغات الأخرى.
المخاطر الأمنية المرتبطة بـ unserialize()
1. Object Injection
واحدة من أخطر الثغرات الأمنية في PHP ترتبط بـ unserialize()، وتعرف باسم حقن الكائنات (Object Injection). تحدث عندما يتم تمرير سلسلة تسلسل غير موثوقة إلى unserialize()، مما قد يسمح للمهاجمين بإنشاء كائنات مصممة خصيصًا لتنفيذ شفرة خبيثة عبر دوال سحرية مثل __wakeup(), __destruct() أو __toString().
مثال:
php$data = $_GET['data']; // سلسلة تسلسل قادمة من المستخدم
$obj = unserialize($data); // خطر أمني محتمل
2. استغلال الملفات والأنظمة
قد تؤدي عملية unserialize لكائن يحتوي على خصائص تتعامل مع ملفات أو قواعد بيانات إلى تسرب بيانات أو تنفيذ أوامر.
وسائل الوقاية من ثغرات unserialize()
-
عدم استخدام
unserialize()على بيانات من مصادر غير موثوقة. -
استخدام
json_encode()وjson_decode()كبديل أكثر أمانًا. -
استخدام
unserialize()مع المعاملallowed_classesلتقييد أنواع الكائنات المقبولة:
php$data = unserialize($input, ["allowed_classes" => ["MySafeClass"]]);
-
فصل الكائنات المنفذة عن الكائنات الحاملة للبيانات.
-
تعطيل دوال سحرية غير ضرورية، أو عدم تضمينها في الكود إذا لم تكن مطلوبة.
التعامل مع النسخ الحديثة من PHP
في الإصدارات الحديثة من PHP 7.0+، أصبحت العديد من التحذيرات والقيود أكثر صرامة في عمليات unserialization. تم تقديم وسيلة allowed_classes كما سبق، وأصبحت دوال سحرية مثل __destruct() تخضع لمراجعة أكبر.
علاوة على ذلك، تم إدخال خصائص readonly وميزات جديدة في PHP 8+ لتحسين الأمان في التعامل مع الكائنات.
تقنيات بديلة لـ Serialization
-
JSON: مثالي لنقل البيانات بين PHP وJavaScript، أو عبر API.
-
XML: قديم نسبيًا وأثقل، لكنه لا يزال مستخدمًا في بعض الأنظمة.
-
YAML: أكثر قابلية للقراءة ولكن يتطلب مكتبة خارجية.
-
MessagePack: صيغة ثنائية أسرع من JSON لكنها أقل قابلية للقراءة.
مثال تطبيقي عملي
phpclass Product {
public $name;
public $price;
public function __construct($n, $p) {
$this->name = $n;
$this->price = $p;
}
}
$product = new Product("Laptop", 4500);
$serialized = serialize($product);
file_put_contents("product.txt", $serialized);
// لاحقًا
$contents = file_get_contents("product.txt");
$restored = unserialize($contents);
echo $restored->name; // Laptop
جدول يوضح مقارنة الاستخدام بين JSON و Serialization في سيناريوهات مختلفة:
| الحالة | الأفضل استخدام | الملاحظات |
|---|---|---|
| تخزين بيانات الجلسة | Serialization | تدعم تخزين كائنات كاملة |
| نقل بيانات عبر HTTP | JSON | متوافق مع معظم اللغات والواجهات |
| تخزين بيانات إعدادات بتنسيق قابل للقراءة | JSON | أسهل في التحرير والفحص البشري |
| حفظ كائنات معقدة في ملفات | Serialization | يحافظ على الهيكل الكامل للكائن |
| التواصل بين PHP و JavaScript | JSON | صيغة معيارية ومشتركة |
الخلاصة
تُعد ميزة Serialization في PHP أداة قوية لتحويل الكائنات وبنى البيانات المعقدة إلى تمثيل قابل للنقل أو التخزين، مما يجعلها ضرورية في العديد من سيناريوهات التطوير. ومع ذلك، فإن الاستخدام الخاطئ لها، خاصة عند unserializing بيانات غير موثوقة، يمكن أن يؤدي إلى مشاكل أمنية خطيرة. لذلك، يجب التعامل مع هذه الوظيفة بحذر، مع فهم تام لآلية عملها وأفضل الممارسات المتعلقة بها.
في المشاريع الحديثة، يُوصى باستخدام JSON كلما أمكن، خاصة عند عدم الحاجة لتسلسل كائنات PHP الكاملة. ومع تقدم إصدارات PHP، أصبحت الأدوات الأمنية المرافقة لعملية serialization أكثر قوة، مما يُمكّن المطور من الاستفادة من هذه التقنية بأمان وكفاءة.
المراجع:

