حماية سلوكيات خادم PHP من ثغرة حقن SQL

تاريخ آخر تحديث 27‏/04‏/2021

المشكلة

تتيح لك بعض قواعد البيانات إرسال نصوص SQL متعددة في استعلام واحد. لذلك، توجد مخاطر أمنية محتملة عندما تمرر المعاملات في سلسلة الاستعلام إلى استعلام قاعدة بيانات مُنشأ ديناميكيًا.يمكن للمتسللين محاولة تعديل متغيرات عنوان URL أو النموذج في استعلام ديناميكي عن طريق إلحاق بيانات SQL ضارة بالمعاملات الموجودة.وغالبًا ما يشار إلى هذا بهجوم حقن SQL. يجب تعديل بعض رموز سلوك الخادم التي أنشأتها Dreamweaver للحد من مخاطر هجمات حقن SQL. لمزيد من المعلومات الأساسية عن حقن SQL، راجع مقال Wikipedia هذا. (Ref. 201713)

ملاحظة: تم إصلاح المشكلة الموضحة في هذه الوثيقة التقنية في محدث Dreamweaver 8.0.2.

تغطي هذه الوثيقة التقنية سلوكيات خادم PHP في Dreamweaver.توجد ملاحظات تقنية منفصلة لسلوكيات الخادم ColdFusion، وASP VBScript، وASP JavaScript، وJSP.سلوكيات خادم ASP.NET لا تتأثر.

الحل

عند السماح لسلسلة استعلام بتمرير معلمة، تأكد من تمرير المعلومات المتوقعة فقط. أنشأت Adobe أداة تحديث Dreamweaver 8.0.2 التي تقلل من مخاطر هجمات حقن SQL.سيتم دمج هذه الإصلاحات في جميع الإصدارات المستقبلية من Dreamweaver.بعد تحديث Dreamweaver 8، ستحتاج إلى إعادة تطبيق سلوكيات الخادم على الصفحات التي تستخدمها، وإعادة نشر تلك الصفحات على خادمك.

يوثق الجزء المتبقي من هذه المذكرة التقنية كيفية تحرير كود Dreamweaver MX 2004 يدوياً لمنع هجمات حقن SQL باستخدام نموذج خادم PHP.سلوكيات الخادم المعرضة لهذه الهجمات مدرجة أدناه، مع الإصلاحات:

مجموعة السجلات مع تصفية

لا تحتاج مجموعات السجلات التي لم تتم تصفيتها إلى تعديل، ولكن تحتاج مجموعات السجلات التي تمت تصفيتها إلى ذلك. في المثال أدناه، تتم تصفية مجموعة سجلات Dreamweaver MX 2004 المسماة "rs_byDate" بواسطة قيمة تاريخ ممررة من معامل عنوان URL.الكود المميز أدناه هو ما يحتاج إلى تغييره:

<?php $colname_rs_byDate = "1"; if (isset($_GET['relDate'])) { $colname_rs_byDate = (get_magic_quotes_gpc()) ?$_GET['relDate'] : addslashes($_GET['relDate']); } mysql_select_db($database_dreamqa2, $dreamqa2); $query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate); $rs_byDate = mysql_query($query_rs_byDate, $dreamqa2) or die(mysql_error()); $row_rs_byDate = mysql_fetch_assoc($rs_byDate); $totalRows_rs_byDate = mysql_num_rows($rs_byDate); ?>

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

انتقل إلى السطر الثاني في عرض الكود، مباشرة بعد ملف التضمين للاتصال، وأضف الدالة GetSQLValueString() إلى بداية الكود كما يلي:

<?php if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") { $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } } ?>

احذف كود علامات الاقتباس السحرية بحيث لا تُضاف الشرطات المائلة العكسية بشكل خاطئ إلى بيانات GET وPOST عندما تكون علامات الاقتباس السحرية مُطفأة على خادم PHP.

الكود الأصلي:

$colname_rs_byDate = (get_magic_quotes_gpc()) ? $_GET['relDate'] : addslashes($_GET['relDate']);

الكود المعدَّل:

$colname_rs_byDate = $_GET[&apos;relDate&apos;];



حدّث كود مجموعة السجلات لإنشاء نص SQL باستخدام الدالة GetSQLValueString() التي تم إنشاؤها أعلاه.حدّث قيمة المتغير $query_rs_byDate:

الكود الأصلي:

$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate);

الرمز الآمن المعدل:

$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = %s", GetSQLValueString($colname_rs_byDate, "date"));

سلوكيات الخادم لإدراج وتحديث وحذف السجلات ومعالج نموذج إدراج السجلات

إن سلوكيات الخادم لإدراج وتحديث وحذف السجلات ومعالج نموذج إدراج السجلات تستخدم بالفعل الدالة GetSQLValueString()، لذا تحتاج إلى تحسين لمنع هجمات حقن SQL.التغييرات الوحيدة المطلوبة هي في الدالة GetSQLValueString().

إليك الكود الأصلي.السطر المراد تغييره مُظلل باللون الأصفر:

if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") {$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue; switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . &quot;&apos;&quot; : &quot;NULL&quot;; break; case &quot;defined&quot;: $theValue = ($theValue != &quot;&quot;) ?$theDefinedValue : $theNotDefinedValue; break; } return $theValue; } }

حدّث تعريف المتغير $theValue:

الكود الأصلي:

$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue;

الكود المعدَّل الآمن:

$theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

تسجيل الدخول إلى سلوك خادم المستخدم

فيما يلي خطوات إصلاح سلوك خادم تسجيل دخول المستخدم:

أنشئ دالة GetSQLValueString() كما هو موضح في مجموعة السجلات مع عامل تصفية أعلاه.

عدّل الكود الذي ينشئ استعلام SQL لتسجيل الدخول.



الكود الأصلي:

$LoginRS__query=sprintf("SELECT username, password FROM login WHERE username='%s' AND password='%s'", get_magic_quotes_gpc() ? $loginUsername : addslashes($loginUsername), get_magic_quotes_gpc() ? $password : addslashes($password));

الكود المعدَّل الآمن:

$LoginRS__query=sprintf(&quot;SELECT username, password FROM login WHERE username=%s AND password=%s&quot;, GetSQLValueString($loginUsername, &quot;text&quot;), GetSQLValueString($password, &quot;text&quot;));

التحقق من سلوك خادم المستخدم الجديد

يتطلب سلوك خادم التحقق من مستخدم جديد إضافة سلوك خادم إدراج سجل إلى الصفحة أولاً. تأتي دالة GetSQLValuesString() مع سلوك خادم إدراج السجل.كل ما يجب فعله هو تحديث GetSQLValuesString() للتعامل بشكل أفضل مع حقن SQL وتحديث سلوك خادم فحص المستخدم الجديد لاستخدام هذه الدالة عند تمرير معامل.

غيّر السطر الأول في الدالة لتحديث دالة GetSQLValuesString() كما هو موضح في تحديث تعريف المتغير $theValue.

عدّل الكود لإنشاء استعلام SQL لتسجيل الدخول لاستخدام GetSQLValuesString() لتمرير المعاملات. في الجزء العلوي من الصفحة، حدد موقع الكود في القسم الذي يبدأ بـ // *** إعادة التوجيه إذا كان اسم المستخدم موجوداً.



الكود الأصلي:

$LoginRS__query = "SELECT username FROM login WHERE username='" . $loginUsername . "'"



الكود المحدَّث الآمن:

$LoginRS__query = sprintf(&quot;SELECT username FROM login WHERE username=%s&quot;, GetSQLValueString($loginUsername, &quot;text&quot;));

مجموعة صفحة التفاصيل الرئيسية

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

ينشئ Dreamweaver مجموعة سجلات مفلترة في صفحة التفاصيل، لذا يجب تحديث كود مجموعة السجلات هذه لاستخدام دالة GetSQLValueString() لتمرير المعاملات و sprintf() لإنشاء نص SQL.

أنشئ دالة GetSQLValueString() كما هو موضح في مجموعة السجلات مع عامل تصفية أعلاه.

حدّث كود إعلان المتغير وأنشئ استعلام SQL باستخدام دالة GetSQLValuesString().



الكود الأصلي:

$recordID = $_GET['recordID']; $query_DetailRS1 = "SELECT * FROM albums WHERE ID = $recordID";



الكود المحدث المؤمن:

$colname_DetailRS1 = "-1"; if (isset($_GET['recordID'])) { $colname_DetailRS1 = (get_magic_quotes_gpc()) ? $_GET['recordID'] : addslashes($_GET['recordID']); } $query_DetailRS1 = sprintf("SELECT * FROM albums WHERE ID = %s", GetSQLValueString($colname_DetailRS1, "int"));

معالج نموذج تحديث السجل

حدّث تعريف المتغير $theValue كما هو موضح في Insert, Update, and Delete Record server behaviors and Record Insertion Form wizard.
حدّث الكود الخاص بـ Recordset المطلوب لنموذج تحديث السجل كما هو موضح في الخطوة 2 من Recordset مع عامل تصفية.

معلومات إضافية

يُرجى الرجوع إلى المقال التالي لمزيد من المعلومات حول حقن SQL: مقال Wikipedia حول حقن SQL.

نشرة Adobe الأمنية: APSB 06-07 ثغرة حقن SQL في Dreamweaver Server Behavior.