تأمين سكربت php من أهم الثغرات
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
اللهم انفعنا بما علمتنا وعلمنا بما ينفعنا انك انت العليم الحكيم
في هذا المقال باذن الله ساتكلم عن اهم الثغرات التي يستخدمها المخترقين لاختراق كودك
الامان هو عامل مهم جدا في اي تطبيق ويب ولا يجب الاغفال عنه, وهذا ما يفعله للاسف بعض المطورين الجدد, سواء بسبب حسن النية او نسيانهم.
سأحاول ان اغطي في هذا المقال الهجمات المستخدمة بتكرار من قبل المخترقين, وبالطبع لا استطيع ان اعدك ان قراءة هذا المقال ستحمي موقعك من كافة انواع الهجمات لكنها ستوفر لك قدر كافي من الحماية للتصدي لهجمات "اطفال السكربتات", كما تعلمون لا وجود لأمان تام في الانترنت لكن درجة امان الموقع يجب ان تختلف حسب محتواه. فاذا كان هدف الشخص انشاء موقع لبنك مثلا او موقع شراء اونلاين فيجب اخذ الامان بعين الاعتبار التي غالبا ستسبب بطء التطبيق. طبعا ساتحدث عن الامر من منظور لغة php وتحديدا تطبيقات الويب بما انها مجال اختصاصي.
تعريف الثغرات:
هي خطأ او نقص في الكود البرمجي يستغله المهاجم لكي يدخل بيانات خبيثة او يوصل لملف ما بغير تصريح.
1- حقن قواعد البيانات | SQL Injection
احد اكثر الثغرات الامنية التي يتم استخدامها,, في البداية يجب توضيح ان قاعدة بيانات mysql يتم برمجتها بعدة اوامر مختلفة ومن امثلة هذه الاوامر
CREATE TABLE users ;
تأتي الثغرة عندما نستقبل بيانات من المستخدم ونقوم بارسالها لقاعدة البيانات وعلى سبيل المثال لنفرض ان سكربت الPHP كتب هكذا
<?PHP
// نستقبل الاسم المرسل عن طريق ال html form
$name = $_POST['name'];
//عمل اتصال بالداتابيس
$db = mysql_connect('localhost', 'username', 'password' 'db_name');
// نقوم بعمل كويري بسيط لادخال الاسم المخزن في قاعدة البيانات mysql -
$query = "insert into user values('".$name."')";
// خطوة اخرى لتنفيذ الكويري
$insert =$db->query($query);
بالطبع هذا ليس سكربت مثالي لكن في سبيل الاختصار امتنعت عن كتابة بعض الاشياء
تطبيق ثغرة sql injection سيكون كمثال ادخال المستخدم في نموذج الhtml بدلا عن اسمه امر برمجي لقواعد بيانات sql على سبيل المثال وليس الحصر
mohamed; drop table users
طبعا سيتعامل مترجم لغة php مع هذا الاسم كاسم عادي ويمرره دون اي مشاكل, ولكن كومبايلر sql سيقوم باضافة اسم mohamed (ما قبل الفاصلة المنقوطة) الى المكان المراد
ثم سيقوم بتنفيذ باقي الكود كانه كود برمجي وسيقوم بالفعل بحذف جدول users.
مثال اخر وارد الحدوث
لنفرض مثلا ان لديك فورم تسجيل دخول, والمستخدم لن يستطيع الدخول الا اذا تطابق اسمه مع احد الاسماء المدخلة بالdatabase , صحيح؟
حسنا الكويري المستخدم في هذا المثال يكون شيء مثل الاتي
//عمل جملة كويري بشروط
$query = "SELECT * FROM users WHERE name = '".$name."' )";
كما ترى عزيزي القارئ الكويري يشترط ان يكون الاسم الذي بقاعدة البيانات مطابقا للاسم الذي ادخلة المستخدم والذي تم تخزينه في متغيير name$ بالطبع الكويري لن ينفذ اذا لم يطابق الشرط
لكن يمكن للمخترق ان يتلاعب ويكتب الاتي على سبيل االمثال
اقتباسmohamed or 1=1
بالطبع كلمة or ستتعامل معها قاعدة البيانات ككلمة مفتاحية وتنفذها, ولو افترضنا ان قاعدة البيانات لم تجد اسم محمد في جداولها ستنتقل للشرط الاخر وهو 1=1 وهو شرط صحيح دائما
لذلك ستقوم قاعدة البيانات بعرض جميع البيانات من جدول user كما بُرمجت سلفا SELECT * التي تعني "عرض الكل", ستكون الجملة بالنسبة لمحرر sql شيء مثل
SELECT * FROM users WHERE name = mohamed or 1=1;
الان بعد ان تعرفنا على خطورة الثغرة وماهيتها ناتي لطريقة تجنبها
- الجمل المهيأة | Prepared statment
مكتبة mysql المدمجة تدعم استخدام الجمل المهياة, استخدامها مفيد ليس فقط من ناحية الامان بل حتى في تسريع الكود خصوصا عندما تتعامل مع بيانات كثيرة او مكدسة
والفكرة منها هو ارسال "قالب" فارغ للكويري, ثم ارسال البيانات بعد ذلك بشكل منفصل
<?php
//عمل اتصال بالداتابيس
$db = mysql_connect('localhost', 'username', 'password' 'db_name');
//انشاء كويري بعلامات استفهام بعدد المدخلات التي سيتم ادخالها
$query = "insert into employees values(?, ?, ?)";
$stmt = $db->prepare($query);
// وكتابة كل المتغيرات التي تريد ادخالها في الداتابيس bind_param ا استدعاء الميثود
//على الترتيب string, string , double هو توضيح انواع البيانات للبيانات المدخلة ssd والغرض من كتابة
$stmt->bind_param("ssd", $name, $last_name, $salary);
// تنفيذ الكويري
$stmt->execute();
تعتبر الطريقة المذكورة بالاعلى هي الامثل لتجنب هجمات الحقن. بالطبع هناك طرق اخرى من اهمها استخدام دالة mysql_real_escape_string .
2- Cross Site Scripting | XSS
ايضا تعتبر هذع الثغرة من الثغرات ذات الانتشار الواسع,, حيث يظهر خطرها عندما يتم اعادة عرض محتوى تم ادخاله من قبل المستخدمين للتطبيق.
XSS ببساطة هي ثغرة تتيح للمهاحم اضافة اكواد جافاسكربت خبيثة لصفحات html التي يعرضها موقعك, وعندما يقوم الضحية بفتح تلك الصفحات سيُنفذ الكود مباشرة بما انه جافاسكربت front-end
من قبل المتصفح بالطبع, مما يعرضك انت والمستخدمين لخطر من سرقة كوكيز او اعادة توجيه او حتى غلق الصفحة, بالطبع جميعنا نعلم بقوة لغة جافا سكربت
على سبيل المثال, لنفترض اننا نستقبل بياناات لاحد الاعضاء
وسكربت php بسيط سيبدو كالاتي:
<?php
echo $_POST['name'];
وفورم html
<html>
<body>
<form action="p.php" method="post">
<input type="text" name="name">
<input type="submit">
</form>
</body>
</html>
كما هو واضح الكود بسيط جدا فقط لتوضيح الثغرة لكن لك ان تتخيل مثلا ان متغيير $_POST['data'] هو عبارة عن نبذة شخصية في بروفايل كل عضو
الخطورة تكمن عند اضافة كود جافاسكربت في الحقل الفارغ عند html
كمثال بسيط
<script>
alert('hi, this is xss')
</script>.
بالطبع المثال هذا يقوم بعرض رسالة منبقة تحوي النص اعلاه بالطبع ستعرض هذه الرسالة عندما يقوم اي شخص بمجرد فتح الصفحة المُصابة بها
حسنا, يبدو انك بدات تدرك ان خطورة الثغرة ليست فقط في عرض رسائل منبثقة, بل تتيح للمهاجم كتابة اي كود جافاسكربت يريد وتنفيذه. كود جافاسكربت يمكن ان يكون قوي جدا في هذه الحالة ومؤذي من سرقة الكوكيز الى اغلاق المتصفح وزرع كيلواجر في متصفح الضحية.
لحسن الحظ,, من السهل الحماية ضد هذا النوع من الهجمات, وبما ان الثغرة تنفذ في المتصفح اي بعد ان يُدخل المُخترق كوده الخبيث سنقوم بفلترة "escaping output" او تخطي الحروف المميزة لكي لا يقوم المتصفح بترجمتها واعتبارها String عادي
- دالة htmlspecialchars
الدالة تاخذ ثلاثة باراميترات اجبارية(وهناك اثنان اختيارية) كالتالي :
//تخزين المدخلات في متغير
$unsave_name = $_POST['name'];
// ساشرح هذا السطر بعد قليل
$save_name = htmlspecialchars($unsave_name, ENT_QUOTES, "utf-8");
// الدالة تتطلب 3 باراميترات 1-المتغير المحتوي على مدخلات المستخدم 2- نوع التأمين 3-لغة الترميز ويفضل اختيار UTF-8
$save_name = htmlspecialchars($unsave_name, ENT_QUOTES, "utf-8");
ستقوم الدالة بتحويل الرموز المفتاحية لمرادفاتها من الكلمات مثلا <SCRIPT> ستعرض كالتالي </script> وبالتالي ستفقد قيمتها البرمجية ولن تنفذ
شرح انواع التأمين الثلاث:
- ENT_COMPAT : سيتم في هذا الننوع تحويل علامات التنصيص الثنائية ( " ) الى الرمز " بينما علامة التنصيص الاحادية ( ' ) لن تتحول.
- ENT_QUOTES: سيتم في هذا الننوع تحويل علامات التنصيص الثنائية ( " ) الى الرمز " بينما علامة التنصيص الاحادية ( ' ) سستحول للرمز '
- ENT_NOQUOTES: لن يتم تحويل اي قيم.
اذا كنت تقوم بجلب مدخلات المستخدم وعرضها بشكل منسق مثلا وهو الارجح غالبا لا احد يعرض صفحة html بدون وسوم او تنسيقات
$unsave_name = "<strong>".$_POST['name']."</strong>";
$save_name = htmlspecialchars($unsave_name, ENT_QUOTES, "utf-8");
ستواجهك مشاكل لان الدالة المذكورة انفا تقوم بتحويل جميع الرموز المفتاحية كذلك < و > , ولن يظهر تنسيق <strong> الذي طبقته. الحل هو ان تستخدم الرموز البديلة < و > يمثلان < و > بالترتيب.
يمكنك ايضا استخدام دالة ()htmlentities لتي تحمل نفس الخصائص وتعمل نفس العمل . وايضا دالة (strip_tags($unsavename .
ملاحظة1 : معظم المتصفحات الحديثة لديها حماية ضد xss لذلك من اراد التجربة فليستخدم internet explorer
ملاحظة2 : هناك نوع اخر من xss يدعى dom-based xss وهو يعتبر ثغرة تخص جافاسكربت والفرونت ايند بشكل عام لذلك امتنعت عن ذكرها
3- Directory File Listing
لنفرض ان لديك لديك مجلد يدعى images داخل مسارك الرئيسي www.example.com.. حسنا انت معرض لخطر دخول غير مُصرح لمجلداتك الفرعية
اذا قام احد المستخدمين بكتابة www.example.com/images في المتصفح سيتم اعادة توجيهم نحو المجلد بكافة محتوايته متاحة لهم
لتجنب ذلك سنقوم بتغيير الاعدادت, غالبا في الاستضافة المشتركة shared hosting لن يكون لديك صلاحية لتعديل ملف php.ini المعني باعدادت لغة php
بدلا من ذلك ستنشئ في المجلد الرئيسي ملف باسم
اقتباسhtaccess.
ملاحظة الملف يبدأ بنقطة وبعدها htaccess
وفكرة هذا الملف انه يمكنك من تعديل الاعدادات في المجلد الروت والمجلدات الفرعية منها الخاصة بك فقط
وستقوم باضافة القيمة الاتيه
IndexIgnore *
والتي ستقوم بدورها بمنع الدخول لكافة المجلدات فرعية.
ملاحظة: يوجد اكثر من امر يمكن كتابته يؤدي الى نفس االنتيجة.
4-Error reporting
عرض رسائل الاخطاء خاصية مفيدة جدا خصوصا عندما يكون تطبيقك قيد التطوير , المشكلة تأتي عندما يكون يصبح موقعك متاح للاستخدام
المشكلة لا تكمن في رسائل الاخطاء نفسها فهي ليست مضرة ولن تعرض سكربتك لخطر الاختراق , لكن بعض الاخطاء قد تعرض معلومات قيمة عن تطبيقك كمثال مسار ملف الاتصال بقاعدة البيانات
اذا قمت باي خطأ في طريقة الاتصال على سبيل المثال لا الحصر , لا تستهن بهذه المعلومات فهي قيمة جدا بالنسة للمخترقين.
مثال بسيط لما يمكن اعرض الاخطاء ان تعرض من معلومات قيمة
اذا قمت بعمل اتصال لقاعدة البيانات عن طريق PDO (Php Data Object) -l بالشكل التالي
<?php
$conX = new PDO("mysql:host=X;dbname=myDB", "root", "password");
بالطبع X ليس اسم هوست صحيح, فالبتالي سيعرض هذا PHP الى خطأ قاتل FATAL ERROR وسيقوم معالج اللغة بعرض الرسالة التالية
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: No such host is known. ' in C:\xampp\htdocs\rfi\conx.php:3 Stack trace: #0 C:\xampp\htdocs\rfi\conx.php(3): PDO->__construct('mysql:host=X;db...', 'root', 'password') #1 {main} thrown in C:\xampp\htdocs\rfi\conx.php on line 3
كما هو واضح قام اباتشي بعرض رسالة خطأ منبهاً فيها ان الهوست غير صحيح, نعم ستُعرض هذه الرسالة لمرتادي موقعك اذا حصل خطأ اثناء الاتصال, محتويً على كلمة المرور واسم الادمن وحتى اسم قاعدة البيانات نفيها مما يجعل امر اختراقها امر سهل جدا اذا حصل. ربما انت لن تقوم بخطأ مثل كتابة اسم هوست خاطئ لكن المخترقين لذيهم طرقهم في "اجبار اباتشي على عرض رسالة خطأ" لذلك من الافضل اغلاق عرض الاخطاء
لحسن الحظ يمكن تدارك الامر بسهولة حيث يمكنك تعديل هذه القيم عن طريق ملف php.ini
بالطبع هذه القيمة تحدد ما اذا كان php سيرسل رسائل الاخطاء للشاشة ام لا. بالطبع اذا كان تطبيقك قيد التطوير يفضل تركها on لكن عند نشر موقعك للعامة يفضل اغلاقها .
واذا لم تكن صاحب السيرفر يمكنك كما ذكرت انفا ان تنشئ ملف htaccess. بالقيم التالية
php_flag display_errors off
قيم اخرى ذات علاقة
error_reporting : تحدد عن طريقها ماهي الاخطاء التي يجب ان تعرض ويفصل تركها كما هي E-ALL
log_errors : هذه القيم تحدد ما اذا كانت الاخطاء ستسجل في ملف لمراجعته لاحقا ينصح بتركها ON دائما
error_log : تحديد مسار الملف الذي ستسجل الاخطاء فيه
اذا لم يكن لديك صلاحية لملف php.ini يمكن اضافة القيم التالية في ملف htaccess.
php_flag log_errors on
php_flag error_log /htdocs/myApp/logs_error.log
يمكنك ايضا اضافة
error_reporting(0);
لكود php الذي تكتبه
نصائح عامة :
1.لا تثق في المُستخدم
قد يبدو كلامي وقح بعض الشيء, لكن عندما تفتح موقعك للعامة انت لا تدري من قد يحاول الولوج لموقعك. ليس كل الناس بالطيبة التي تظن.
قم بالتاكد من مدخلات المستخدم وفلترتها مهما كانت بسيطة, فمثلا عند عمل فورم بالشكل التالي
بالكود البسيط التالي
<p> what is your gender
<form action="pr.php" method="post" >
<input type="radio" name="male" > Male
<input type="radio" name="female" > Female
<input type="submit" >
</form>
حسنا,, ولنفترض ان برمجة سكربت الphp تمت بالطريقة التالية لاستقبال النتائج :
<?php
if ($_POST['male'])
echo "you are a male";
elseif ($_POST['female'])
echo "you are female";
على الرغم من انهم خيارين ولا ثالث لهم radio buttons الا ان بها ثغرة, حيث يمكن لاحد المخترقين ان يقوم بإتصال مباشر مع سكربت php ويقوم بارسال قيم غير المحددة ذكر وانثى في حالتنا
كما تعلم عزيزي القارئ بما اننا نستخدم بروتوكول http والذي هو بروتوكول نصي بسيط, لنفرض ان المستخدم اختار male سترسل رسالة لسيرفر الموقع (http) على الصيغة الاتية تقريبا:
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.1)
Gecko/2008070208 Firefox/3.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
Content-Type: text/html; charset=male
حسنا,, يمكن للمخترق ان يقوم باتصال telnet لسيرفرك ويرسل رسالة مشابهه للنص السابق لنفرض مثلا كالاتي
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.1)
Gecko/2008070208 Firefox/3.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
Content-Type: text/html; charset=drop table users
بالطبع سيقوم السيرفر بمعالجة البيانات واذا لم نقم بكتابة كود امن بشكل كافي فربما نتعرض لخطر sql injection اذا كانت البيانات تدخل مباشرة لقاعدة البيانات.
ملاحظة في المثال المذكور اعلاه لا يوجد ادخال في قاعدة البيانات, لكن افترض اسوا الحالات
الحل الامثل لمثل هذه الحالة, ببساطة اضافة هذا السطر لنهاية الكود :
else
exit();
فانت بهذا تكون قد حددت الحالتين الرئيسيتين وفي حال حصول اي حالة اخرى سيقوم السكربت باغلاق نفسه exit(); تجنبا للهجمات.
- قم بفلترة كافة مدخلات المستخدم بالدوال المذكورة انفاً.
- قم بانشاء اكثر من ادمن لقاعدة البيانات بصلاحيات مختلفة, عند انشاء اتصال لا تتصل عن طريق الروت دائما. فاذا كان الغرض من الاتصال اضافة بيانات جديدة فقط فقم باستخدام الاكاونت الذي لديه صلاحية الinsert فقط وهكذا دواليك.
- تحقق حتى من ابسط المدخلات والحالات, تذكر قاعدة بيانات mysql حساسة جدا وادخال بيانات خاطئة بها قد يسبب مشاكل حقيقية لك,
- تأكد من النص المدخل ليس فارغ اذا كان الحقل المقابل في قاعدة البيانات not null
- تاكد من ان النص لا يتجاوز عدد الحروف المسموح به في قاعدة البيانات.
- (اختياري) تأكد من ان النص لا يحتوي على الرموز الغريبة مثل #$%*@: وغيرها يمكن ان تحقق هذا ببساطة باستخدام التعابير القياسية.
لاختبار تطبيقك ينصح بتجربة جميع الاخطاء المذكورة فيه لترى بنفسك,
كمثال :
- يمكنك ان تدخل بيانات مثل delete table users; في تطيقك
- جرب ان تدخل بيانات مثل $%%#@@
- جرب ان تدخل نوع بيانات غير المحددة في قاعدة البيانات كادخال احرف لاتينية مثلا في حقل من نوع date.
صل الله وسلم على سيدنا محمد وعلى اله وصحبه اجمعين.
بحمد الله انتهيت من ذكر اكثر اربع هجمات شيوعا واغلبها متعلقة بالكود البرمجي, باذن الله في الموضوع القادم ساكتب عن اربع ثغرات اخرى باذن الله.
لايوجد لديك حساب في عالم البرمجة؟
تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !