إساءة استخدام التضمين
1. سبب الثغرة الأمنية:
التضمين هو الوظيفة الأكثر استخدامًا في كتابة مواقع PHP ويدعم المسارات النسبية. هناك العديد من نصوص PHP التي تستخدم مباشرة متغير إدخال كمعلمة تضمين، مما يسبب ثغرات أمنية مثل المرجع التعسفي للبرنامج النصي وتسرب المسار المطلق. انظر إلى الكود التالي:
...
$includepage=$_GET["includepage"];
تشمل($includepage);
...
من الواضح أننا نحتاج فقط إلى إرسال متغيرات Includepage مختلفة للحصول على الصفحة المطلوبة. إذا قمت بإرسال صفحة غير موجودة، فمن الممكن أن تتسبب في حدوث خطأ في برنامج PHP النصي وتسريب المسار المطلق الفعلي (تم شرح حل هذه المشكلة في المقالة التالية).
2. حل نقاط الضعف:
الحل لهذه الثغرة الأمنية بسيط جدًا، وهو تحديد ما إذا كانت الصفحة موجودة أولاً ثم تضمينها. أو بشكل أكثر دقة، استخدم مصفوفة لتحديد الملفات التي يمكن تضمينها. انظر إلى الكود التالي:
$pagelist=array("test1.php"،test2.php"،test3.php"); // يحدد هذا الملفات التي يمكن تضمينها
if(isset($_GET["includepage"])) // تحديد ما إذا كان هناك $includepage
{
$includepage=$_GET["includepage"];
foreach($pagelist كـ $prepage)
{
if($includepage==$prepage) //تحقق مما إذا كان الملف موجودًا في القائمة المسموح بها
{
تشمل($الصفحة المسبقة);
$checkfind=true;
استراحة؛
}
}
إذا($checkfind==true){ unset($checkfind });
else{ die("صفحة مرجعية غير صالحة!");
}
وهذا سوف يحل المشكلة بشكل جيد للغاية.
نصيحة: تتضمن الوظائف التي بها هذه المشكلة: require() و require_once() و include_once() و readfile() وما إلى ذلك. ويجب عليك أيضًا الانتباه عند الكتابة.
لا يتم تصفية متغيرات الإدخال
1. سبب الثغرة الأمنية:
ظهرت هذه الثغرة الأمنية منذ فترة طويلة في ASP، مما تسبب في عدد لا يحصى من نقاط الضعف في الحقن في ذلك الوقت. ولكن نظرًا لأن تأثير PHP كان صغيرًا في ذلك الوقت، لم يتمكن الكثير من الناس من الاهتمام بهذا الأمر. بالنسبة لـ PHP، يكون تأثير هذه الثغرة الأمنية أكبر من تأثير ASP، نظرًا لأن المزيد من نصوص PHP تستخدم قواعد بيانات نصية. بالطبع، هناك أيضًا مشكلة حقن عبارات SQL. لإعطاء مثال أكثر كلاسيكية، الأول هو قاعدة البيانات:
$id=$_GET["id"];
$query="SELECT * FROM my_table Where id='".$id."'"; // ثغرة أمنية في حقن SQL
$result=mysql_query($query);
من الواضح هنا أنه يمكننا استخدام الحقن للحصول على محتويات أخرى لقاعدة البيانات. لن أصفها بالتفصيل هنا، تمامًا مثل حقن ASP، يمكنك إلقاء نظرة على الدفاعات السوداء السابقة. ثم ننظر إلى مشكلة قواعد البيانات النصية:
$text1=$_POST["text1"];
$text2=$_POST["text2"];
$text3=$_POST["text3"];
$fd=fopen("test.php"،"a");
fwrite($fd,"rn$text1&line;$text2&line;$text3");
فكلوز($fd);
ويمكن القول إن ضعف النص أكثر خطورة. إذا قمنا بإدخال جزء صغير من كود PHP في المتغير المقدم، فيمكننا تحويل قاعدة بيانات النص هذه test.php إلى باب خلفي PHP. حتى أن إدخال رمز التحميل يسمح لنا بتحميل باب خلفي PHP كامل. ثم قم بزيادة الامتيازات ويصبح الخادم لك.
2. حل نقاط الضعف:
الحل لهذه الثغرة الأمنية بسيط جدًا في الواقع، وهو تصفية جميع المتغيرات المقدمة بشكل صارم. استبدال بعض الشخصيات الحساسة. يمكننا استبدال محتوى HTML بمساعدة الدالة htmlspecialchars() التي توفرها PHP. هنا مثال:
// إنشاء وظيفة التصفية www.knowsky.com
الدالة flt_tags($text)
{
$badwords=array("اللعنة عليك"،"اللعنة"); // قائمة مرشحات الكلمات
$text=rtrim($text);
foreach($badwords as $badword) // تصفية المفردات هنا
{
if(stristr($text,$badword)==true){ die("خطأ: المحتوى الذي أرسلته يحتوي على كلمات حساسة، يرجى عدم إرسال محتوى حساس.");
}
$text=htmlspecialchars($text); //استبدال HTML
// يستبدل هذان الخطان حرف الإرجاع بـ
$text=str_replace("r"،"
",$نص);
$text=str_replace("n"،"،$text);
$text=str_replace("&line;"،"،$text); // استبدل فاصل قاعدة بيانات النص "&line;" بالعرض الكامل "│"
$text=preg_replace("/s{ 2 }/"، "،$text); // استبدال المسافة
$text=preg_replace("/t/"،"،$text); // لا يزال يتم استبدال المسافات
if(get_magic_quotes_gpc()){ $text=stripslashes($text } // إذا تم تشغيل الاقتباسات السحرية، فاستبدل '
إرجاع نص $؛
}
$text1=$_POST["text1"];
$text2=$_POST["text2"];
$text3=$_POST["text3"];
// تصفية جميع المدخلات
$text1=flt_tags($text1);
$text2=flt_tags($text2);
$text3=flt_tags($text3)
;
fwrite($fd,"rn$text1&line;$text2&line;$text3");
فكلوز($fd);
بعد إجراء بعض عمليات الاستبدال والتصفية، يمكنك كتابة البيانات بأمان إلى نص أو قاعدة بيانات.
حكم المسؤول غير مكتمل
1. سبب الثغرة الأمنية:
نحن نستخدم PHP لكتابة البرامج النصية، والتي عادة ما تتضمن أذونات المسؤول. تصدر بعض البرامج النصية حكم "نعم" فقط على أذونات المسؤول، ولكنها غالبًا ما تتجاهل الحكم "لا". عند تشغيل Register_globals في ملف تكوين PHP (يتم إيقاف تشغيله افتراضيًا في الإصدارات ما بعد 4.2.0، ولكن يقوم العديد من الأشخاص بتشغيله من أجل الراحة، وهو سلوك خطير للغاية)، ستكون هناك مواقف يتم فيها إرسال المتغيرات لانتحال صفة المسؤولين. دعونا نلقي نظرة على رمز المثال:
$cookiesign="admincookiesign"; // تحديد ما إذا كان متغير ملف تعريف الارتباط الخاص بالمسؤول أم لا
$adminsign=$_COOKIE["sign"]; // احصل على متغير ملف تعريف الارتباط الخاص بالمستخدم
if($adminsign==$cookiesign)
{
$admin=true;
}
if($admin){ echo "أنت الآن مسؤول.";
يبدو آمنا جدا، هاها. الآن نفترض أن Register_globals قيد التشغيل في ملف تكوين PHP. لقد أرسلنا هذا العنوان "test.php?admin=true"، هل رأيت النتيجة؟ على الرغم من أننا لا نملك ملف تعريف الارتباط الصحيح، نظرًا لأنه تم تشغيل Register_globals، يتم تسجيل متغير المسؤول الذي أرسلناه تلقائيًا على أنه صحيح. علاوة على ذلك، يفتقر البرنامج النصي إلى حكم "لا"، مما يسمح لنا بالحصول على أذونات المسؤول بنجاح من خلال admin=true. هذه المشكلة موجودة في أغلب المواقع والمنتديات
2. حل نقاط الضعف:
لحل هذه المشكلة، نحتاج فقط إلى إضافة حكم "لا" إلى المسؤول في البرنامج النصي. ما زلنا نفترض أن Register_globals قيد التشغيل في ملف تكوين PHP. ألق نظرة على الكود:
$cookiesign="admincookiesign"; // تحديد ما إذا كان متغير ملف تعريف الارتباط الخاص بالمسؤول أم لا
$adminsign=$_COOKIE["sign"]; // احصل على متغير ملف تعريف الارتباط الخاص بالمستخدم
if($adminsign==$cookiesign)
{
$admin=true;
}
آخر
{
$admin=false;
}
if($admin){ echo "أنت الآن مسؤول.";
بهذه الطريقة، حتى إذا أرسل المهاجم المتغير admin=true بدون ملف تعريف الارتباط الصحيح، فسيقوم البرنامج النصي بتعيين $admin على False في الأحكام المستقبلية. وهذا يحل جزءا من المشكلة. ومع ذلك، نظرًا لأن $admin متغير، إذا حدثت ثغرة في مراجع البرامج النصية الأخرى في المستقبل وتمت إعادة تعيين $admin، فستحدث أزمة جديدة. لذلك، يجب علينا استخدام الثوابت لتخزين تحديد أذونات المسؤول. استخدم عبارة Define() لتحديد ثابت المسؤول لتسجيل أذونات المسؤول. إذا تمت إعادة تعيينه بعد ذلك، فسيحدث خطأ يحقق غرض الحماية. انظر إلى الكود التالي:
$cookiesign="admincookiesign"; // تحديد ما إذا كان متغير ملف تعريف الارتباط الخاص بالمسؤول أم لا
$adminsign=$_COOKIE["sign"]; // احصل على متغير ملف تعريف الارتباط الخاص بالمستخدم
if($adminsign==$cookiesign)
{
تعريف (المشرف، صحيح)؛
}
آخر
{
تعريف(المشرف,خطأ);
}
if(admin){ echo "أنت الآن في حالة المسؤول.";
تجدر الإشارة إلى أننا نستخدم عبارة Define، لذلك عند استدعاء ثابت Admin، لا تقم عادةً بإضافة رمز المتغير $ في المقدمة، ولكن استخدم Admin و !admin.
كشفت قاعدة البيانات النصية
1. سبب الثغرة الأمنية:
كما ذكرنا سابقًا، نظرًا للمرونة الكبيرة لقواعد البيانات النصية، لا يلزم وجود دعم خارجي. بالإضافة إلى ذلك، تتمتع PHP بقدرات قوية جدًا على معالجة الملفات، لذلك تُستخدم قواعد البيانات النصية على نطاق واسع في نصوص PHP النصية. حتى أن هناك العديد من برامج المنتديات الجيدة التي تستخدم قواعد البيانات النصية. ولكن هناك مكاسب وخسائر، كما أن أمان قواعد البيانات النصية أقل من قواعد البيانات الأخرى.
2. حل نقاط الضعف:
تعمل قاعدة البيانات النصية كملف عادي يمكن تنزيله، تمامًا مثل MDB. لذلك، نحتاج إلى حماية قواعد البيانات النصية بنفس طريقة حماية بنوك التنمية المتعددة الأطراف. قم بتغيير اسم اللاحقة لقاعدة البيانات النصية إلى .PHP. والانضمام إلى الصف الأول من قاعدة البيانات. بهذه الطريقة سيتم التعامل مع قاعدة البيانات النصية كملف PHP وسيتم الخروج من التنفيذ في السطر الأول. أي أنه يتم إرجاع صفحة فارغة لتحقيق غرض حماية قاعدة البيانات النصية.
تسربت المسار الخاطئ
1. سبب الثغرة الأمنية:
عندما يواجه PHP خطأ ما، فإنه سيعطي الموقع ورقم السطر وسبب خطأ البرنامج النصي، على سبيل المثال:
ملاحظة: استخدام اختبار ثابت غير محدد - يُفترض وجود "اختبار" في D:interpubbigflytest.php في السطر 3
يقول الكثير من الناس إنها ليست مشكلة كبيرة. لكن عواقب تسريب المسار الفعلي لا يمكن تصورها بالنسبة لبعض المتسللين، وهذه المعلومات مهمة للغاية. في الواقع، تعاني العديد من الخوادم الآن من هذه المشكلة.
يقوم بعض مسؤولي الشبكات ببساطة بتعيين أخطاء العرض في ملف تكوين PHP على إيقاف لحل المشكلة، لكنني أعتقد أن هذه الطريقة سلبية للغاية. في بعض الأحيان، نحتاج حقًا إلى PHP لإرجاع معلومات الخطأ لتصحيح الأخطاء. وعندما يحدث خطأ ما، قد تحتاج أيضًا إلى تقديم تفسير للمستخدم أو حتى الانتقال إلى صفحة أخرى.
2. حل نقاط الضعف:
لقد وفرت PHP دالة معالجة الأخطاء المخصصة ()set_error_handler منذ 4.1.0، لكن القليل من كتاب النصوص يعرفون ذلك. من بين العديد من منتديات PHP، لم أر سوى القليل منها يتعامل مع هذا الموقف. استخدام set_error_handler كما يلي:
سلسلة set_error_handler (رد الاتصال error_handler [، int error_types])
نستخدم الآن معالجة الأخطاء المخصصة لتصفية المسارات الفعلية.
// المشرف هو تحديد هوية المسؤول، صحيح هو المسؤول.
// يجب أن تحتوي وظيفة معالجة الأخطاء المخصصة على متغيرات الإدخال الأربعة $errno، $errstr، $errfile، $errline، وإلا فستكون غير صالحة.
الدالة my_error_handler($errno,$errstr,$errfile,$errline)
{
// إذا لم تكن مسؤولاً، قم بتصفية المسار الفعلي
إذا (! المشرف)
{
$errfile=str_replace(getcwd(),"",$errfile);
$errstr=str_replace(getcwd(),"",$errstr);
}
التبديل($errno)
{
الحالة E_ERROR:
صدى "خطأ: [ID $errno] $errstr (السطر: $errline لـ $errfile)
n";
echo "توقف البرنامج عن التشغيل، الرجاء الاتصال بالمسؤول.";
// اخرج من البرنامج النصي عند مواجهة خطأ في مستوى الخطأ
مخرج؛
استراحة؛
حالة E_WARNING:
صدى "تحذير: [ID $errno] $errstr (السطر: $errline of $errfile)
n";
استراحة
؛
// لا تعرض أخطاء مستوى الإشعار
استراحة؛
}
}
// قم بتعيين معالجة الأخطاء على وظيفة my_error_handler
set_error_handler("my_error_handler");
…
بهذه الطريقة، يمكن حل التناقض بين الأمان وسهولة تصحيح الأخطاء بشكل جيد. ويمكنك أيضًا التفكير في جعل رسالة الخطأ أكثر جمالًا لتتناسب مع نمط موقع الويب. لكن لاحظ نقطتين:
(1) لن تتم معالجة E_ERROR وE_PARSE وE_CORE_ERROR وE_CORE_WARNING وE_COMPILE_ERROR وE_COMPILE_WARNING بواسطة هذا المقبض، أي أنه سيتم عرضها بالطريقة الأكثر أصالة. ومع ذلك، تحدث هذه الأخطاء بسبب أخطاء التجميع أو أخطاء PHP kernel ولن تحدث في الظروف العادية.
(2) بعد استخدام set_error_handler()، سيكون تقرير الخطأ () غير صالح. أي أنه سيتم تسليم جميع الأخطاء (باستثناء الأخطاء المذكورة أعلاه) إلى الوظيفة المخصصة للمعالجة.
للحصول على معلومات أخرى حول set_error_handler()، يمكنك الرجوع إلى دليل PHP الرسمي.
ما بعد الضعف
1. سبب الثغرة الأمنية:
كما ذكرنا سابقًا، يعد الاعتماد على Register_globals لتسجيل المتغيرات عادة سيئة. في بعض برامج سجل الزوار والمنتديات، من الضروري التحقق بدقة من طريقة الحصول على الصفحات والفاصل الزمني بين عمليات الإرسال. لمنع المشاركات غير المرغوب فيها والإرسالات الخارجية. دعونا نلقي نظرة على الكود التالي لبرنامج سجل الزوار:
...
$text1=flt_tags($text1);
$text2=flt_tags($text2);
$text3=flt_tags($text3)
;
fwrite($fd,"rn$text1&line;$text2&line;$text3");
فكلوز($fd);
...
من الواضح، إذا أرسلنا عنوان URL "post.php?text1=testhaha&text2=testhaha&text3=testhaha". سيتم كتابة البيانات إلى الملف بشكل طبيعي. لا يكتشف هذا البرنامج مصدر المتغيرات وكيفية حصول المتصفح على الصفحة. إذا أرسلنا عدة إرسالات إلى هذه الصفحة، فسوف يتسبب ذلك في حدوث فيضانات. كما أن هناك بعض البرامج التي تستغل هذه الثغرة لنشر إعلانات على المنتديات أو سجل الزوار، وهذا سلوك مشين (لقد امتلأ سجل الزوار الخاص بصديقي بأكثر من 10 صفحات في أسبوع واحد، وهو أمر لا حول له ولا قوة).
2. حل نقاط الضعف:
قبل معالجة البيانات وحفظها، حدد أولاً كيفية حصول المتصفح على الصفحة. استخدم المتغير $_SERVER["REQUEST_METHOD"] للحصول على طريقة المتصفح للحصول على الصفحة. تحقق مما إذا كان "POST". استخدم الجلسة في البرنامج النصي لتسجيل ما إذا كان المستخدم يرسل البيانات من خلال القنوات العادية (أي الصفحة التي يتم فيها ملء محتوى الإرسال). أو استخدم $_SERVER["HTTP_REFERER"] لاكتشاف ذلك، ولكن هذا غير مستحسن. نظرًا لأن بعض المتصفحات لا تقوم بتعيين REFERER، فإن بعض جدران الحماية ستحظر أيضًا REFERER. بالإضافة إلى ذلك، نحتاج أيضًا إلى التحقق من المحتوى المقدم لمعرفة ما إذا كان هناك محتوى مكرر في قاعدة البيانات. خذ سجل الزوار كمثال، استخدم الجلسة لاتخاذ القرار:
في الصفحة التي تملأ فيها محتوى التصفح، نضيف في الواجهة الأمامية:
$_SESSION["allowgbookpost"]=time(); // الوقت الذي يتم فيه ملء التسجيل. في الصفحة التي يتم فيها قبول بيانات الرسالة وحفظها، نستخدم أيضًا الجلسة لإجراء المعالجة التالية قبل معالجة البيانات:
if(strtoupper($_SERVER["REQUEST_METHOD"])!="POST"){ die("خطأ: لا ترسل خارجيًا.") // تحقق مما إذا كانت طريقة الحصول على الصفحة هي POST
if(!isset($_SESSION["allowgbookpost"]) أو (time()-$_SESSION["allowgbookpost"] < 10)){ die("خطأ: لا ترسل خارجيًا.") // تحقق من الرسالة الوقت عند ملء
if(isset($_SESSION["gbookposttime"]) and (time()-$_SESSION["gbookposttime"] < 120)){ die("خطأ: يجب ألا يقل الفاصل الزمني بين إرسالين للرسائل عن دقيقتين. "); } // تحقق من الفاصل الزمني للرسائل
unset($_SESSION["allowgbookpost"]); // قم بإلغاء تسجيل متغيرallowgbookpost لمنع عمليات الإرسال المتعددة من الدخول إلى صفحة التعبئة في وقت واحد
$_SESSION["gbookposttime"]=time(); // سجل وقت إرسال الرسائل لمنع البريد العشوائي أو الهجمات الضارة
...
معالجة البيانات وتخزينها
...
بعد هذه المراجعات المتعددة، سيكون برنامجك أكثر أمانًا.