البرمجة

السلسلة في PHP: الدليل الكامل

السلسلة 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).

php
class User { public $name; private $password; public function __construct($name, $password) { $this->name = $name; $this->password = $password; } } $user = new User("Omar", "1234"); $serialized = serialize($user);

الناتج يكون كالتالي:

css
O: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() على كائن. يُمكنك تحديد أسماء الخصائص التي ينبغي تسلسلها.

php
class Config { public $db; private $cache; public function __sleep() { return ['db']; // فقط خاصية db سيتم تسلسلها } }

__wakeup()

تُستخدم عند استدعاء unserialize() لإعادة تهيئة الكائن بعد استعادته من السلسلة.

php
class 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 لتخزين البيانات المركبة:

php
apcu_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 لكنها أقل قابلية للقراءة.


مثال تطبيقي عملي

php
class 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 أكثر قوة، مما يُمكّن المطور من الاستفادة من هذه التقنية بأمان وكفاءة.


المراجع:

  1. PHP Manual – serialize

  2. OWASP – PHP Object Injection