هل يحافظ htmlspecialchars وmysql_real_escape_string على كود PHP الخاص بي آمنًا من الحقن؟

StackOverflow https://stackoverflow.com/questions/110575

سؤال

في وقت سابق من اليوم تم طرح سؤال بخصوص استراتيجيات التحقق من صحة المدخلات في تطبيقات الويب.

الإجابة العليا، في وقت كتابة هذا التقرير، تشير إلى PHP مجرد استخدام htmlspecialchars و mysql_real_escape_string.

سؤالي هو:هل هذا يكفي دائما؟هل هناك المزيد يجب أن نعرفه؟أين تنهار هذه الوظائف؟

هل كانت مفيدة؟

المحلول

عندما يتعلق الأمر باستعلامات قاعدة البيانات، حاول دائمًا استخدام الاستعلامات ذات المعلمات المعدة.ال mysqli و PDO المكتبات تدعم هذا.هذا أكثر أمانًا بشكل لا نهائي من استخدام وظائف الهروب مثل mysql_real_escape_string.

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

تخيل SQL التالية:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);

يجب أن تكون قادرًا على رؤية أن هذا عرضة للاستغلال.
تخيل id تحتوي المعلمة على ناقل الهجوم الشائع:

1 OR 1=1

لا يوجد أي أحرف محفوفة بالمخاطر لتشفيرها، لذلك سوف تمر مباشرة عبر مرشح الهروب.يغادرنا:

SELECT fields FROM table WHERE id= 1 OR 1=1

وهو عبارة عن ناقل حقن SQL جميل وسيسمح للمهاجم بإرجاع جميع الصفوف.أو

1 or is_admin=1 order by id limit 1

الذي ينتج

SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1

مما يسمح للمهاجم بإعادة تفاصيل المسؤول الأول في هذا المثال الخيالي تمامًا.

على الرغم من أن هذه الوظائف مفيدة، إلا أنه يجب استخدامها بحذر.تحتاج إلى التأكد من التحقق من صحة جميع مدخلات الويب إلى حد ما.في هذه الحالة، نرى أنه يمكن استغلالنا لأننا لم نتحقق من أن المتغير الذي كنا نستخدمه كرقم، كان في الواقع رقميًا.في PHP، يجب عليك استخدام مجموعة من الوظائف على نطاق واسع للتحقق من أن المدخلات هي أعداد صحيحة، أو أعداد عائمة، أو أبجدية رقمية، وما إلى ذلك.ولكن عندما يتعلق الأمر بـ SQL، انتبه جيدًا لقيمة العبارة المعدة.كان من الممكن أن يكون الكود أعلاه آمنًا إذا كان بيانًا مُعدًا لأن وظائف قاعدة البيانات كانت ستعرف ذلك 1 OR 1=1 ليس حرفيًا صالحًا.

أما بالنسبة لل htmlspecialchars().وهذا حقل ألغام في حد ذاته.

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

أولاً، إذا كنت داخل علامة HTML، فأنت في مشكلة حقيقية.ينظر الى

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

نحن بالفعل داخل علامة HTML، لذا لا نحتاج إلى < أو > للقيام بأي شيء خطير.يمكن أن يكون ناقل الهجوم الخاص بنا كذلك javascript:alert(document.cookie)

الآن يبدو HTML الناتج

<img src= "javascript:alert(document.cookie)" />

الهجوم يمر مباشرة.

تزداد الأمور سوءا.لماذا؟لأن htmlspecialchars (عند الاتصال بهذه الطريقة) يقوم فقط بتشفير علامات الاقتباس المزدوجة وليس المفردة.لذلك إذا كان لدينا

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";

يستطيع مهاجمنا الشرير الآن إدخال معايير جديدة تمامًا

pic.png' onclick='location.href=xxx' onmouseover='...

يعطينا

<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />

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

حتى لو كنت تستخدم htmlspecialchars($string) خارج علامات HTML، لا تزال عرضة لهجوم ناقلات الأحرف متعددة البايت.

الطريقة الأكثر فاعلية هي استخدام مجموعة من mb_convert_encoding وhtmlentities على النحو التالي.

$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');

وحتى هذا يترك IE6 عرضة للخطر، بسبب الطريقة التي يتعامل بها مع UTF.ومع ذلك، يمكنك الرجوع إلى ترميز أكثر محدودية، مثل ISO-8859-1، حتى يتوقف استخدام IE6.

للحصول على دراسة أكثر تعمقًا لمشاكل البايتات المتعددة، راجع https://stackoverflow.com/a/12118602/1820

نصائح أخرى

بالإضافة إلى إجابة Cheekysoft الممتازة:

  • نعم، سوف تحافظ على سلامتك، ولكن فقط إذا تم استخدامها بشكل صحيح تمامًا.استخدمها بشكل غير صحيح وستظل عرضة للخطر، وقد تواجه مشاكل أخرى (على سبيل المثال تلف البيانات)
  • الرجاء استخدام الاستعلامات ذات المعلمات بدلاً من ذلك (كما هو مذكور أعلاه).يمكنك استخدامها من خلال على سبيل المثال.PDO أو عبر غلاف مثل PEAR DB
  • تأكد من إيقاف تشغيل Magic_quotes_gpc وmagic_quotes_runtime في جميع الأوقات، وعدم تشغيلهما عن طريق الخطأ أبدًا، ولا حتى لفترة وجيزة.هذه محاولة مبكرة ومضللة للغاية من قبل مطوري PHP لمنع المشكلات الأمنية (التي تدمر البيانات)

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

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

سأتفق بالتأكيد مع المشاركات المذكورة أعلاه، ولكن لدي شيء صغير يجب إضافته ردًا على إجابة Cheekysoft، على وجه التحديد:

عندما يتعلق الأمر باستعلامات قاعدة البيانات ، حاول دائمًا استخدام استعلامات معلمة معدّة.تدعم مكتبات Mysqli و PDO هذا.هذا أكثر أمانًا بلا حدود من استخدام وظائف الهروب مثل mysql_real_escape_string.

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

تخيل SQL التالية:

$ result = "حدد الحقول من الجدول حيث id =" .mysql_real_escape_string ($ _ post ['id']) ؛

يجب أن تكون قادرًا على رؤية أن هذا عرضة للاستغلال.تخيل أن معلمة المعرف تحتوي على متجه الهجوم المشترك:

1 أو 1=1

لا يوجد أي شوائب محفوفة بالمخاطر للتشفير ، لذلك سوف يمر مباشرة عبر الفلتر الهرب.يغادرنا:

حدد الحقول من الجدول حيث معرف = 1 أو 1 = 1

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

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

لذلك بدلا من استخدام

$result = "اختر الحقول من الجدول حيث المعرف = ".mysqlrealescapestring("1 OR 1=1");

سأستخدم

$result = "اختر الحقول من الجدول حيث المعرف = ".Numbers("1 OR 1=1");

وسيتم تشغيل الاستعلام بأمان

حدد الحقول من الجدول حيث المعرف = 111

بالتأكيد، أدى ذلك إلى منعه من عرض الصف الصحيح، لكنني لا أعتقد أن هذه مشكلة كبيرة لمن يحاول إدخال SQL في موقعك؛)

جزء مهم من هذا اللغز هو السياقات.لا يمثل إرسال شخص ما "1 OR 1=1" كمعرف مشكلة إذا قمت باقتباس كل وسيطة في استعلامك:

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"

مما يؤدي إلى:

SELECT fields FROM table WHERE id='1 OR 1=1'

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

$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

يعمل بشكل جيد، وحتى أفضل على أنظمة 64 بت.احذر من قيود أنظمتك على معالجة الأعداد الكبيرة، ولكن بالنسبة لمعرفات قاعدة البيانات، فإن هذا يعمل بشكل رائع بنسبة 99% من الوقت.

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

لماذا، أوه لماذا، هل لا تضمين علامات الاقتباس حول إدخال المستخدم في بيان SQL الخاص بك؟يبدو سخيفا جدا لا!تضمين علامات الاقتباس في بيان SQL الخاص بك من شأنه أن يجعل "1 أو 1=1" محاولة غير مثمرة، أليس كذلك؟

والآن، ستقول، "ماذا لو قام المستخدم بتضمين علامة اقتباس (أو علامات اقتباس مزدوجة) في الإدخال؟"

حسنًا، حل سهل لذلك:ما عليك سوى إزالة علامات الاقتباس التي أدخلها المستخدم.على سبيل المثال: input =~ s/'//g;.الآن، يبدو لي على أي حال، أنه سيتم تأمين إدخال المستخدم ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top