ثغرات سرقة الجلسة : session hijacking، session fixation

ثغرات سرقة الجلسة : session hijacking، session fixation


معظم المواقع التفاعلية تعتمد على الجلسات "sessions" لإعطاء ميزات للأعضاء المنخرطين . يتم تخزين بيانات الجلسة على الخادوم و يتم ربط كل عضو بمُعرّف ID خاص به . حماية بيانات العضو ترتكز كليا على هذا المُعرّف الذي يبقى صلة الربط بين الخادوم و المتصفح . المُعرّف هو سلسلة من مكونات النص يتم إنتاجها عشوائيا ، غالبا تضم 32 حركة و يمكن التحكم في إنتاجها من خلال بعض التعليمات مثل : session.hash_function, session.entropy_length و session.entropy_file سنتعرف عليها قريبا في هذا الدرس .
نعرف بأن بروتوكول HTTP غير مستقر و لا يجعل أي ربط بين الإستعلامات أو الطلبات التي يُرسلها المتصفح للخادوم ، ليتذكر بأن نفس العضو هو الذي يرسل الإستعلامات يبقى مُعرّف ID الجلسة هو البصمة الأساسية للتعرف على العضو ، و هذا يجعل من المعرّف هدفا مغريا لأي مهاجم ينوي اختلاس ميزات الأعضاء . بمجرد الحصول عليه يمكن للمهاجم الولوج للموقع مستعملا جلسة الضحية و فعل ما يحلو له دون حاجة لكلمة مرور .

يمكن للمهاجم انتحال شخصية عضو ما عبر تقنيات عدة ، معظمها ينظوي تحت قسم يسمى "session hijacking" .
على العموم لا الحصر يمكن تمييز ثلاث تقنيات الأكثر استعمالا لانتحال شخصية العضو الضحية عبر الحصول على معرف ID الخاص به
  • Prediction: التخمين
  • Capture: الأسر
  • Session fixation: تثبيت الجلسة
  • session hijacking: الإستحواذ على الجلسة

Prediction : هذه الطريقة هي الأقل استعمالا و الأقل خطورة ، لأنها تعتمد على التخمين و من الصعب تخمين معرفات IDs للجلسات .
Capture: طريقة أسر و ضبط معرف الجلسة تبقى جد مستعملة ، و تعتمد على عدة مقاربات . عندما يكونالكوكيز هو المستعمل لنقل معرف الجلسة ، يمكن استغلال ثغرات الحماية كثغرة XSS للحصول على المعرّف . و عندما يكون عنوان الويب URL هو المستعمل ، هنا يصبح الخطر أكبر و حظوظ المهاجم أوفر لسرقة المعرّف عبر تقنيات عدة . لهذا يعتبر استعمال الكوكيز لنقل معرف ID الجلسة أكثر أمنا من نقله عبر عنوان الويب .
Session fixation : باستعمال هذه التقنية ، لا يقوم المهاجم باختلاس أو تخمين معرف الجلسة للضحية ، بل يرغم الضحية على استعمال مُعرف ID قام هو باختياره .
session hijacking : باستعمال عدة تقنيات يمكن للمهاج الإستحواذ على معرف ID لجلسة الضحية .

هذا هو محور درسنا الذي سنحيط فيه بالنقاط التالية :
  1. تعريف ثغرتي Session fixation و session hijacking
  2. بعض تقنيات استغلال الثغرات
  3. حماية ثغرات الجلسة
كيف أعرف بأن موقعي يضم ثغرات الجلسة ؟
الجواب سهل جدّا . إذا كان نظام الجلسات لديك يعتمد فقط على التعليمة session_start() لبدء الجلسة ، فموقعك مهدد و يتوفر على الثغرة .

1 . تثبيت الجلسة : Session fixation

عندما يملأ العضو استمارة الدخول (الإسم أو البريد الإلكتروني زائد كلمة المرور) للولوج لحسابه ، يتم إرسال طلبه إلى الخادوم الذي يقوم بمعالجة البيانات و مقارنتها . إذا وجد البيانات صحيحة يتأكد من أن العضو ليس لديه معرف ID للجلسة التي طلبها . إذا كان الأمر كذلك يقوم بتزويده بمعرف ID جديد . و هذا المعرف هو بمثابة بطاقة التعريف للعضو بين المتصفح و الخادوم و عليه يتم الإعتماد لتزويد العضو بجميع الميزات المخولة له .

في حالة وجود ثغرة "تثبيت الجلسة" = "Session fixation" . يستطيع المهاجم تزويد الضحية بمعرف ID حتى قبل أن يملأ هذا الأخير ملأ استمارة الدخول . و عندما يملأها و يرسلها للخادوم ، يلاحظ هذا الأخير بأن العضو الذي طلب تسجيل دخوله يتوفر مسبقا على معرف ID لجلسته . و يتقاعس عن تزيده بمعرف جديد بل يُلصق لجلسته المُعرف الذي تم تقديمه مع الطلب ، أي المُعرّف الذي اختاره المهاجم . (هكذا هو تصرف خادوم PHP إذ يسمح بقبول أي مُعرّف ID يُقدّم له ، بخلاف خادوم IIs الذي يعتبر صارما في هذا المجال و لا يقبل إلا مُعرفات الجلسة التي يتم إنتاجها محليا أي داخل الخادوم و هذا لا يعني بأنه محمي من الثغرة).
لم يبقى للمهاجم سوى الدخول للموقع المعني و تقديم معرّف ID السابق للخادوم ، حتى ينتحل شخصية الضحية و يحصل على جميع ميزاته .
Session fixation attack : يقوم المهاجم بتثبيت جلسة الضحية حتى قبل أن يرسل هذا الأخير طلب الدخول للخادوم . و بهذا يمنع الخادوم من تزويد العضو بمُعرّف ID آخر غير الذي تم إرساله مع الطلب .

1.1 استغلال ثغرة Session fixation

تقنيات استغلال هذه الثغرة تختلف حسب النظام المستعمل لتداول معرف الجلسة بين الخادوم والمتصفح : توجد على الأقل ثلاث طرق معروفة و هي : إما إرسال معرف الجلسة عبر عنوان الويب URL ، أو عبر الإستمارة في حقل خفي أو الطريقة الأكثر استعمالا عبر الكوكيز .
سنرى أسفله بعض الإستغلال لمختلف الحالات

1.1.1 نقل معرف ID عبر عنوان الويب URL

يعتمد نظام الجلسات على نقل معرف ID (الذي نسميه في هذه الحالة trans-id) عبر عنوان الويب في مفتاح PHPSESSID . مثلاPHPSESSID=1t189vj65u8skg15mlo76njqj7
لنفترض موقع بنك حيث يمكن للمنخرطين القيام بعملياتهم البنكية .
الصورة أسفله توضح مراحل عملية تثبيت جلسة الضحية


1. يبدء المهاجم بتسجيل دخوله بصفة قانونية على موقع : http://online.somebank.kom
2. بعد ربط الإتصال بالموقع ، يزوده الخادوم بمفتاح لمعرف ID الجلسة : PHPSESSID=1234
3. ثم يرسل وصلة أو رابطا يضم معرف ID السابق للضحية . و بطريقة ما يغريها بالنقر على الوصلة :
<a href="http://online.somebank.kom/login.php?PHPSESSID=1234">أنقر هنا</a>
- عندما تنقر الضحية على الرابط الذي سيفتح صفحة استمارة الدخول للبنك على متصفحها ، في هذه المرحلة قبل حتى أن ترسل الإستمارة تكون قد حصلت على معرف ID لجلستها عبر العنوان الملغوم . و سيتم إرساله للخادوم .
4. عندما يتوصل الخادوم ببيانات الإستمارة يتأكد من كلمة مرور الضحية مع باقي بيانات الإستمارة و يلاحظ بأن الضحية لديها مسبقا معرف ID و لا تحتاج لواحد آخر . يوافق على بياناتها و يسمح لها بالولوج لحسابها البنكي و تكون الضحية قد استعملت جلسة المهاجم .
5. في هذه المرحلة ، بما أن المهاجم يعرف معرّف الجلسة ، لأنه هو الذي قدّمه للضحية ، سيستعمل نفس المعرف ID للولوج لحسابها account.php?PHPSESSID=1234 ، أما الخادوم فسيعتبر أن الإستعلام قدمه نفس الشخص .

تعتبر هذه الطريقة من أبسط طرق استغلال ثغرة تثبيت الجلسة ، و يجب أن تتوفر بعض العوامل لنجاح العملية : أولا يجب على المهاجم أن يقوم بالهجوم باستعمال نفس خادوم الضحية و يجب أيضا الإحتيال لإقناع الضحية للنقر على الرابط . الطرق الأخرى التي سنراها أسفله ، متقدمة نوعا ما ، لأنها لا تثير الإنتباه كثيرا و من الصعب على الضحية ملاحظة عملية الإستغلال ، كما تتيح للمهاجم بدء الهجوم من أي خادوم آخر . لن أركز عليها كثيرا ، لأن الهدف هو الإلمام بطرق استغلال الثغرة حتى نستطيع حمايتها بشكل جيد .

1.1.2 نقل معرف ID عبر الكوكيز

في هذه الحالة ، يستعمل نظام الجلسات الكوكيز لنقل معرف ID بين الخادوم والمتصفح . و هذا النظام هو المستعمل من طرف جل المواقع ، لأنه من الأرجح الإعتقاد بأنه أكثر أمنا من عناوين الويب لكن من جهة أخرى في حالة إمكانية استغلاله يكون أكثر ملاءمة و سرية و فعالية و ديمومة لتمكين المهاجم من إتمام عملية الإستغلال بنجاح .
نعرف بأن المتصفح لا يقبل إلا الكوكيزات المتعلقة بالخادوم المصدر أو من نطاقاته . مثلا موقع على خادوم www.attacker.com لا يمكنه إضافة كوكيزات لموقع على خادوم آخرonline.victime.com
لكن يمكن للمهاجم فعل ذلك باتباع إحدى الطرق الثلاث لإضافة الكوكيز إلى متصفح الضحية :
  1. إستعمال السكريبت
  2. إستعمال وسم <META>
  3. إستعمال HTTP header response

إستعمال السكريبت

هذه الطريقة ممكنة فقط في حالة وجود ثغرة XSS . حتى يتمكن المهاجم من تثبيت كوكيز يحمل معرف ID على متصفح الضحية و ذلك بإضافة السكريبت في عنوان URL الذي سيتم إرساله للضحية كالتالي : http://online.somebank.kom/<script>document.cookie=”PHPSESSID=1234”;</script>

إستعمال وسم <META>

ترتكز أيضا على وجود ثغرة XSS . و توضع الشيفرة أيضا في عنوان الويب الذي سيتم إرساله للضحية : http://online.somebank.kom/<meta%20http-equiv=Set-Cookie%20content=”PHPSESSID=1234”;%20Expires=Wed,%2001-Sep-2015%2000:00:00%20GMT”>

إستعمال HTTP header response

يمكن للمهاجم إعتراض البيانات المتداولة بين المتصفح و الخادوم باستعمال إحدى البرانم المختصة . و تثبيت قيمة الكوكيز في ترويسات HTTP

1.2 حماية ثغرة Session fixation

يمكننا حماية ثغرة تثبيت الجلسة بطريقة سهلة ، بما أن المهاجم يعتمد على تثبيت معرف ID حتي يستعمله الضحية . المنطق هو عدم قبول هذا المعرف ، و لتفعيل ذلك يجب أن نطلب من الخادوم تعيين معرف ID جديد كلما أراد العضو الولوج لحسابه أو أثناء القيام بأي عملية حساسة تتطلب التعرف عليه .
توجد تعليمة PHP جاهزة و مخصصة لذلك ، و هي : session_regenerate_id()
هذه التعليمة سترغم الخادوم على إنتاج معرف ID جديد . لكن سيتم الإحتفاظ أيضا ببيانات الجلسة القديمة . لتدمير بيانات الجلسة السالفة يجب إضافة قيمة true للتعليمة ، كالتالي :
<?php 
session_start();
session_regenerate_id(true);
يجب استعمال session_regenerate_id في كل صفحة تحتاج للعمليات الحساسة ، مثل :
أثناء التعرف على هوية العضو (استمارات الدخول) .
أثناء إضافة بيانات حساسة للجلسة .
أثناء تغيير أي شيء يتعلق بالجلسة .
أثناء تغيير ميزات العضو : مثلا أثناء الولوج للوحة المشرف أو للوحة المدير ...
- يمكن أيضا إضافة بعض الحماية لملف php.ini إذا كان خادومكم يتيح لكم الولوج إليه ، إبحث عن التعليمتين أسفله في الملف و وضبطهما كالتالي :
  • session.use_trans_sid = 0 : القيمة "0" ستمنع PHP من إقحام و قراءة المعرفات القادمة عبر عنوان URL .
  • session.use_only_cookies = 1 : القيمة "1" ستمنع PHP من استعمال عناوين URL لنقل معرفات الجلسة . و سيتم نقلها عبر الكوكيز فقط .
- أما إذا كان خادومكم لا يسمح لكم الولوج لملف php.ini يمكن تفعيلهما أيضا عبر htaccess. أو سكريبت php
<?php
ini_set('session.use_trans_sid', 0);
ini_set('session.use_only_cookies', 1);

session_start();
session_regenerate_id(true);


- بالنسبة للذين لديهم استضافة مشتركة (موقعهم موجود مع مجموعة أخرى من المواقع على نفس الخادوم) ، يجب تغيير مكان تخزين بيانات الجلسة على الخادوم ، فهذا الأخير يستعمل مبدئيا ملف "/tmp" . و هذا الملف متاح للجميع بحيث يمكن الولوج إليه قراءة و كتابة . الحل هو استعمال ملف آخر على نطاق استضافتكم أو من الأفضل استعمال قاعدة البيانات لتخزين بيانات الجلسة و ذلك باستعمال session_set_save_handler .

لنتعرف على الثغرة الأخرى و هي Session Hijacking لا تقل خطورة عن ثغرة تثبيت الجلسة ، التي تنضوي تحت قسمها . الفرق بينهما هو :
Session fixation : المهاجم هو الذي يقدم معرف الجلسة للضحية .
Session Hijacking : يقوم المهاجم بحيازة معرف جلسة الضحية .

2. Session Hijacking

تتمحور هذه الثغرة حول إمكانية المهاجم الإستحواذ على معرّف الجلسة "ID" للضحية و ذلك باتباع إحدى الطريقتين :
  • Active Session Hijacking:
    سرقة الجلسات النشيطة : تتطلب هذه الطريقة أن يكون الضحية متصلا (سجل دخوله بنجاح في الموقع) . يمكن للمهاجم استغلال ثغرة XSS و إرسال رابط للضحية يتضمن سكريبت ملغوم للحصول على محتوى كوكيز الجلسة ، و تستدعي أيضا إغراء الضحية للنقر على الرابط .
  • Passive Session Hijacking:
    هذه الطريقة أخطر من الأولى ، لأنها من جهة لا تتطلب بالأساس وجود الثغرة السابقة و لا تستدعى أي تواصل مع الضحية . يعتمد المهاجم تقنية Session Sniffing و ذلك باستعمال إحدى البرانم المخصصة لذلك ، و تتجلى هذه التقنية باعتراض رزم البيانات "packet" المتبادلة بين تطبيقات الويب و الخادوم ثم تمحيصها لإيجاد ما يبحث عنه .

2.1 حماية ثغرة Session Hijacking

توجد عدة مقاربات لتعقيد المهمة على المهاجم و الرفع من نظام حماية الجلسات رغم أن هذه المقاربات بعضها لا يخلو من سلبيات ، سأحاول طرحها و من ثم يعود لكم القرار في اختيار الوجهة المناسبة لكم .

الإعتماد على نوع المتصفح : User-Agent

User-Agent : هو نص يصف نوع المتصفح الذي تستخدمونه و يتم إرساله عبر ترويسات HTTP Headers إلى الخادوم .
مثال لترويسة HTTP
GET /profile.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (compatible; MSIE)
Accept: text/xml, image/jpeg, image/png, image/gif, */*
Cookie: PHPSESSID=1234
تعتمد هذه المقاربة على أنه إذا تغيرت قيمة User-Agent فهذا يعني أن الإستعلام تم تقديمه من طرف المهاجم (من متصفح آخر غير متصفح الضحية) مستعملا جلسة الضحية . لهذا يجب أن ندمر الجلسة حتى لا يستغلها و نحوله لصفحة تسجيل الدخول مثلا . لتفعيل هذه الحماية عبر سكريبت php :
<?php
/** صفحة تسجيل الدخول بنجاح **/
/**
...
*/

session_start();
session_regenerate_id(true);

$_SESSION['USER_AGENT'] = sha1($_SERVER['HTTP_USER_AGENT']);

/** و إضافة الشيفرة أسفله في باقي الصفحات **/
/**
...
*/

if (isset($_SESSION['USER_AGENT']) AND $_SESSION['USER_AGENT'] !== sha1($_SERVER['HTTP_USER_AGENT'])) {
/* تدمير الجلسة */
exit;
}
هذه المقاربة ليست فعالة مائة بالمائة ففي أغلب الحالات المهاجم الذي استطاع الحصول على مُعرّف الجلسة يمكنه أيضا الحصول على قيمة User-Agent و جميع بيانات ترويسات HTTP .
إذا كنت تميل لاستعمال User-Agent رغم ذلك ، يمكنك تعقيد المهمة على المهاجم و ذلك بإضافة بصمة له . البصمة عبارة عن تشفير نضيفه عندما يسجل العضو دخوله بنجاح ، ثم نعتمد عليها للتأكد من وجودها في الصفحات الأخرى . بهذه الطريقة حتى إن حصل المهاجم على بيانات User-Agent يجب عليه أن يبذل مجهودا آخر لإيجاد البصمة و نوع التشفير المستخدم :
<?php /** login-success.php **/
session_start();
session_regenerate_id(true);

$fingerprint = sha1('addAnySaltTextHere' . $_SERVER['HTTP_USER_AGENT']);
$_SESSION['fingerprint'] = $fingerprint;
ثم في باقي الصفحات يجب التأكد من أن الشيفرة هي نفسها :
<?php 
session_start();

$fingerprint = sha1('addAnySaltTextHere' . $_SERVER['HTTP_USER_AGENT']);

if(isset($_SESSION['fingerprint']) AND $_SESSION['fingerprint'] !== $fingerprint) {
$_SESSION = array();
session_destroy();
header('location: login.php');
exit;
}

الإعتماد على Token

بدل الإعتماد على User-Agent . يمكنك إنتاج شيفرة فريدة "token" عندما يسجل العضو دخوله بنجاح . مثال :
$token = md5(uniqid(rand(),TRUE));

$_SESSION['token'] = $token;
من الأفضل تمرير قيمة الشيفرة الفريدة إلى جميع الصفحات إما عبر عناوين الويب أو الإستمارات بدل الكوكيز . حتى إن تمكن المهاجم الحصول على بيانات HTTP Header . لن يتمكن من الحصول على الشيفرة الفريدة .
__ يجب تخزين الشيفرة الفريدة بمجرد إنتاجها في مكان ما ، مثلا في قاعدة البيانات حتى تتمكنوا من مقارنتها .
__ إستعملت "uniqid" للتشفير كمثال فقط ، تشفير هذه الدالة ضعيف جدا لأنه يعتمد على الوقت . لإنتاج شيفرة متينة يمكن إستعمال openssl_random_pseudo_bytes

الإعتماد على بروتوكول الأنترنت IP

هناك أيضا من يعتمد على عنوان بروتوكول الأنترنت IP . و هذه التقنية جد فعالة ، لأن لكل مستخدم بروتوكول IP فريد . و إذا تغير هذا البروتوكول ، فهذا يعني "نظريا" أن مستخدما آخر يستغل الجلسة .
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
لكن مع وجود الكثير من المستخدمين الذين يتوفرون على بروتوكولات غير قارة و تتغير من حين لآخر (خلال نفس الجلسة يمكن لبروتوكول IP أن يتغير لنفس المستخدم) أظن أنه من الغير الأخلاقي تدمير جلسات أعضاء بريئين . نعم للحماية لكن ليس على حساب فعالية آداء و مصداقية الموقع .

توظيب تعليمات الجلسة

لإضافة طبقات أخرى من الحماية سنكمل بإجراء تعديلات في ملف php.ini لمن لديهم إمكانية الولوج إليه (غالبا الذين يتوفرون على خادومهم الخاص) .
بالنسبة للذين لا يستطعيون الولوج لملف php.ini ستجدون أسفل هذه الفقرة سكريبت php يمكنكم من تفعيل أغلب هذه التعليمات .

إما في ملف php.ini

  • قم بتفعيل session.cookie_httponly = 1حتى تمنع الولوج لمحتوى الكوكيز عبر السكريبتات التي تستغل ثغرة XSS .
  • إستعمل أقوى تشفير لمعرّف الجلسة ، و ذلك بتعديل قيمة session.hash_function
    إذا كان رقم إصدار PHP على خادومك أقل من 5.3 إستعمل session.hash_function = 1
    بالنسبة للإصدارات PHP >= 5.3 إستعمل إما session.hash_function = sha256 أو session.hash_function = sha512
  • إستعمل أقوى تشفير للتعليمةsession.hash_bits_per_character = 5هذا يجعل إمكانية إيجاد مُعرّف الجلسة عبر التخمين مستحيلا .
  • تغيير قيمتي : session.entropy_file وsession.entropy_length
    session.entropy_file = /dev/urandomو بالنسبة للتعليمة الثانية ، مثلا :
    session.entropy_length = 256
  • غير الإسم session_name() الإفتراضي للجلسة PHPSESSID إلى إسم من إختيارك . إفعل ذلك عبر سكريبت php أنظر السكريبت أسفله .

أو عبر سكريبت php

<?php
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_lifetime', 0);//0 = يعني أن مدة صلاحية الجلسة تنتهي عند إغلاق المتصفح . عدلوا الوقت حسب رغبتكم
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.hash_function', 'sha512');
ini_set('session.use_only_cookies', 1);
ini_set('session.hash_bits_per_character', 5);
//ini_set('session.cookie_secure', 1); // SSL (HTTPS)

session_name('cid');// إختر إسما قصيرا مكونا من حروف لاتينية و أرقام فقط و يضم على الأقل حرفا واحدا
session_start();
session_regenerate_id(true);

أو في مستند htaccess.

إليك مثالا لتفعيل التعليمات في ملف htaccess ، و أكمل الباقي :
<IfModule php5_module>
php_flag session.cookie_httponly on
</IfModule>

خلاصة

خلاصة القول ، أنصحك كثيرا من حماية موقعك جيدا من الثغرات الشائعة على الويب ، مثل : XSS , CSRF, SQL Injection,... و إذا كان لديك موقع يتداول بيانات حساسة ، كموقع تجاري مثلا يجب شراء رخصة SSL ليتم تبادل بيانات الموقع و الخادوم بطريقة آمنة و تجنبك أيضا الهجوم عبر session sniffing .
أتمنى أن يكون هذا الدرس قد استوفى هدفه ، سنتعرف لاحقا بحول الله على ثغرات أخرى أكثر شيوعا و خطورة على الويب