سؤال

إذا تم إدراج إدخال المستخدم دون تعديل في استعلام SQL، يصبح التطبيق عرضة للاختراق حقن SQL, ، كما في المثال التالي:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

ذلك لأنه يمكن للمستخدم إدخال شيء من هذا القبيل value'); DROP TABLE table;--, ، ويصبح الاستعلام:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

ما الذي يمكن فعله لمنع حدوث ذلك؟

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

المحلول

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

لديك في الأساس خياران لتحقيق ذلك:

  1. استخدام شركة تنمية نفط عمان (لأي برنامج تشغيل قاعدة بيانات مدعوم):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. استخدام MySQLi (لـ MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

إذا كنت تتصل بقاعدة بيانات أخرى غير MySQL، فهناك خيار ثانٍ خاص ببرنامج التشغيل يمكنك الرجوع إليه (على سبيل المثال. pg_prepare() و pg_execute() لـ PostgreSQL).PDO هو الخيار العالمي.

إعداد الاتصال بشكل صحيح

لاحظ أنه عند استخدام PDO للوصول إلى قاعدة بيانات MySQL حقيقي البيانات المعدة هي لا تستخدم بشكل افتراضي.لإصلاح ذلك عليك تعطيل محاكاة البيانات المعدة.مثال على إنشاء اتصال باستخدام PDO هو:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

في المثال أعلاه، وضع الخطأ ليس ضروريًا تمامًا، لكن ينصح بإضافته.بهذه الطريقة لن يتوقف البرنامج النصي عند Fatal Error عندما يحدث خطأ ما.ويعطي المطور الفرصة لذلك catch أي خطأ (أخطاء) التي throwن كما PDOExceptionس.

ما هو إلزامي, ومع ذلك فهو الأول setAttribute() السطر الذي يطلب من شركة تنمية نفط عمان تعطيل البيانات المُعدة التي تمت محاكاتها واستخدامها حقيقي البيانات المعدة.يؤدي ذلك إلى التأكد من عدم تحليل PHP للبيان والقيم قبل إرسالها إلى خادم MySQL (لا يمنح أي مهاجم محتمل أي فرصة لإدخال SQL ضار).

على الرغم من أنه يمكنك ضبط charset في خيارات المُنشئ، من المهم ملاحظة أن الإصدارات "الأقدم" من PHP (<5.3.6) تجاهل بصمت المعلمة مجموعة الأحرف في DSN.

توضيح

ما يحدث هو أن عبارة SQL التي تمرر إليها prepare يتم تحليلها وتجميعها بواسطة خادم قاعدة البيانات.من خلال تحديد المعلمات (إما أ ? أو معلمة مسماة مثل :name في المثال أعلاه) تخبر محرك قاعدة البيانات بالمكان الذي تريد التصفية فيه.ثم عند الاتصال execute, ، يتم دمج البيان المعد مع قيم المعلمات التي تحددها.

الشيء المهم هنا هو أن يتم دمج قيم المعلمات مع العبارة المترجمة، وليس سلسلة SQL.يعمل حقن SQL عن طريق خداع البرنامج النصي لتضمين سلاسل ضارة عندما يقوم بإنشاء SQL لإرسالها إلى قاعدة البيانات.لذلك، من خلال إرسال SQL الفعلي بشكل منفصل عن المعلمات، فإنك تحد من خطر الانتهاء من شيء لم تكن تقصده.سيتم التعامل مع أي معلمات ترسلها عند استخدام عبارة معدة كسلاسل فقط (على الرغم من أن محرك قاعدة البيانات قد يقوم ببعض التحسين لذلك قد تنتهي المعلمات كأرقام أيضًا بالطبع).في المثال أعلاه، إذا كان $name يحتوي المتغير 'Sarah'; DELETE FROM employees ستكون النتيجة ببساطة البحث عن السلسلة "'Sarah'; DELETE FROM employees", ، ولن ينتهي بك الأمر طاولة فارغة.

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

أوه، وبما أنك سألت عن كيفية القيام بذلك للإدراج، إليك مثال (باستخدام PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

هل يمكن استخدام البيانات المعدة للاستعلامات الديناميكية؟

بينما لا يزال بإمكانك استخدام البيانات المعدة لمعلمات الاستعلام، لا يمكن تحديد معلمات بنية الاستعلام الديناميكي نفسه ولا يمكن تحديد معلمات معينة للاستعلام.

بالنسبة لهذه السيناريوهات المحددة، أفضل ما يمكنك فعله هو استخدام مرشح القائمة البيضاء الذي يقيد القيم المحتملة.

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

نصائح أخرى

تحذير مهمل:يستخدم نموذج التعليمات البرمجية لهذه الإجابة (مثل نموذج التعليمات البرمجية للسؤال) لغة PHP MySQL الامتداد، والذي تم إهماله في PHP 5.5.0 وإزالته بالكامل في PHP 7.0.0.

تحذير الأمان:هذه الإجابة لا تتماشى مع أفضل الممارسات الأمنية. الهروب غير كاف لمنع حقن SQL, ، يستخدم البيانات المعدة بدلاً من.استخدم الإستراتيجية الموضحة أدناه على مسؤوليتك الخاصة.(أيضًا، mysql_real_escape_string() تمت إزالته في PHP 7.)

إذا كنت تستخدم إصدارًا حديثًا من PHP، فإن mysql_real_escape_string لن يكون الخيار الموضح أدناه متاحًا بعد الآن (على الرغم من ذلك mysqli::escape_string هو المعادل الحديث).هذه الأيام mysql_real_escape_string سيكون الخيار منطقيًا فقط بالنسبة للتعليمات البرمجية القديمة في إصدار قديم من PHP.


لديك خياران - الهروب من الشخصيات الخاصة الموجودة في شخصيتك unsafe_variable, أو باستخدام استعلام ذو معلمات.كلاهما من شأنه أن يحميك من حقن SQL.يعتبر الاستعلام ذو المعلمات أفضل ممارسة ولكنه سيتطلب التغيير إلى ملحق MySQL أحدث في PHP قبل أن تتمكن من استخدامه.

سنقوم بتغطية سلسلة التأثير الأقل التي تهرب أولاً.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

شاهد أيضاً تفاصيل mysql_real_escape_string وظيفة.

لاستخدام الاستعلام ذو المعلمات، تحتاج إلى استخدام MySQLi بدلا من ماي إس كيو إل المهام.لإعادة كتابة المثال الخاص بك، سنحتاج إلى شيء مثل ما يلي.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

ستكون الوظيفة الرئيسية التي تريد قراءتها هناك mysqli::prepare.

أيضًا، كما اقترح الآخرون، قد تجد أنه من المفيد/الأسهل تكثيف طبقة من التجريد باستخدام شيء مثل شركة تنمية نفط عمان.

يرجى ملاحظة أن الحالة التي سألت عنها هي حالة بسيطة إلى حد ما وأن الحالات الأكثر تعقيدًا قد تتطلب أساليب أكثر تعقيدًا.بخاصة:

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

كل إجابة هنا تغطي جزءًا فقط من المشكلة.في الواقع، هناك أربعة أجزاء استعلام مختلفة يمكننا إضافتها إليها ديناميكيًا:-

  • سلسلة
  • رقم
  • معرف
  • كلمة أساسية في بناء الجملة.

والبيانات المعدة لا تغطي سوى اثنين منها.

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

بشكل عام، يعتمد نهج الحماية هذا على القائمة البيضاء.

في هذه الحالة، يجب أن يتم ترميز كل معلمة ديناميكية في البرنامج النصي الخاص بك واختيارها من تلك المجموعة.على سبيل المثال، للقيام بالترتيب الديناميكي:

$orders  = array("name", "price", "qty"); // Field names
$key     = array_search($_GET['sort'], $orders)); // See if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

ومع ذلك، هناك طريقة أخرى لتأمين المعرفات - الهروب.طالما أن لديك معرفًا مقتبسًا، يمكنك الهروب من العلامات الخلفية بالداخل عن طريق مضاعفتها.

كخطوة أخرى، يمكننا استعارة فكرة رائعة حقًا لاستخدام بعض العناصر النائبة (وكيل لتمثيل القيمة الفعلية في الاستعلام) من البيانات المعدة وابتكار عنصر نائب من نوع آخر - عنصر نائب للمعرف.

لذا، لجعل القصة الطويلة قصيرة:انه العنصر النائب, ، لا تصريح معد يمكن اعتبارها رصاصة فضية.

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

ومع ذلك، لا تزال هناك مشكلة في الكلمات الأساسية لبناء جملة SQL (مثل AND, DESC وما إلى ذلك)، ولكن يبدو أن القائمة البيضاء هي النهج الوحيد في هذه الحالة.

تحديث

على الرغم من وجود اتفاق عام حول أفضل الممارسات فيما يتعلق بحماية حقن SQL، إلا أن هناك ولا تزال هناك العديد من الممارسات السيئة أيضًا. وبعضها متجذر بعمق في أذهان مستخدمي PHP.على سبيل المثال، في هذه الصفحة بالذات توجد (رغم أنها غير مرئية لمعظم الزوار) أكثر من 80 إجابة محذوفة - تمت إزالتها كلها من قبل المجتمع بسبب سوء الجودة أو الترويج لممارسات سيئة وعفا عليها الزمن.والأسوأ من ذلك أن بعض الإجابات السيئة لا يتم حذفها، بل تزدهر.

على سبيل المثال، هناك(1) هي (2) لا يزال(3) كثير(4) الإجابات(5), ، بما في ذلك ثاني أكثر إجابة التصويت نقترح عليك الهروب اليدوي من السلسلة - وهو أسلوب قديم ثبت أنه غير آمن.

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

أعتقد أن كل هذا بسبب خرافة قديمة جدًا تدعمها مثل هذه السلطات OWASP أو دليل PHP, ، الذي يعلن المساواة بين كل ما هو "الهروب" والحماية من حقن SQL.

بغض النظر عما قاله دليل PHP على مر العصور، *_escape_string بأي حال من الأحوال يجعل البيانات آمنة ولم يكن المقصود ذلك أبدًا.إلى جانب كونه عديم الفائدة لأي جزء SQL بخلاف السلسلة، فإن الهروب اليدوي خطأ، لأنه يدوي على عكس الآلي.

ويزيد الأمر سوءًا OWASP، مع التركيز على الهروب إدخال المستخدم وهو محض هراء:لا ينبغي أن تكون هناك مثل هذه الكلمات في سياق حماية الحقن.كل متغير يحتمل أن يكون خطيرًا - بغض النظر عن المصدر!أو بمعنى آخر - يجب تنسيق كل متغير بشكل صحيح ليتم وضعه في استعلام - بغض النظر عن المصدر مرة أخرى.إنها الوجهة التي تهم.في اللحظة التي يبدأ فيها المطور بفصل الأغنام عن الماعز (التفكير فيما إذا كان بعض المتغيرات المعينة "آمنة" أم لا)، فإنه يتخذ خطوته الأولى نحو الكارثة.ناهيك عن أنه حتى الصياغة تقترح هروبًا جماعيًا عند نقطة الدخول، مما يشبه ميزة الاقتباسات السحرية للغاية - التي تم احتقارها وإهمالها وإزالتها بالفعل.

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

إذا كنت لا تزال غير مقتنع، إليك الشرح الذي كتبته خطوة بخطوة، دليل Hitchhiker لمنع حقن SQL, حيث شرحت كل هذه الأمور بالتفصيل وقمت بتجميع قسم مخصص بالكامل للممارسات السيئة والكشف عنها.

أنصح باستخدام شركة تنمية نفط عمان (كائنات بيانات PHP) لتشغيل استعلامات SQL ذات معلمات.

لا يؤدي هذا إلى الحماية من حقن SQL فحسب، بل يعمل أيضًا على تسريع الاستعلامات.

وباستخدام شركة تنمية نفط عمان بدلاً من mysql_, mysqli_, ، و pgsql_ وظائف، تجعل تطبيقك أكثر تجريدًا من قاعدة البيانات، في حالة نادرة يتعين عليك تبديل موفري قاعدة البيانات.

يستخدم PDO والاستفسارات المعدة.

($conn هو PDO هدف)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

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

كنت أواجه هذه المشكلة، ولكن أعتقد أنني قمت بحلها جداً طريقة متطورة - الطريقة التي يستخدمها المتسللون لتجنب استخدام علامات الاقتباس.لقد استخدمت هذا بالتزامن مع البيانات المعدة مقلدًا.أنا استخدامها لمنع الجميع أنواع هجمات حقن SQL المحتملة.

نهجي:

  • إذا كنت تتوقع أن يكون الإدخال عددًا صحيحًا، فتأكد من ذلك حقًا عدد صحيح.في لغة متغيرة النوع مثل PHP، هذا هو الحال جداً مهم.يمكنك استخدام هذا الحل البسيط جدًا ولكنه قوي على سبيل المثال: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • إذا كنت تتوقع أي شيء آخر من integer عرافة ذلك.إذا قمت بإخفائها، فسوف تفلت تمامًا من كل المدخلات.في C/C++ هناك وظيفة تسمى mysql_hex_string(), ، في PHP يمكنك استخدامه bin2hex().

    لا تقلق بشأن أن السلسلة التي تم هروبها سيكون لها حجم 2x من طولها الأصلي لأنه حتى لو كنت تستخدمها mysql_real_escape_string, يجب على PHP تخصيص نفس السعة ((2*input_length)+1), وهو نفس الشيء.

  • غالبًا ما تُستخدم هذه الطريقة السداسية عند نقل البيانات الثنائية، لكنني لا أرى سببًا لعدم استخدامها على جميع البيانات لمنع هجمات حقن SQL.لاحظ أنه يجب عليك إضافة البيانات مسبقًا باستخدام 0x أو استخدم وظيفة MySQL UNHEX بدلاً من.

لذلك، على سبيل المثال، الاستعلام:

SELECT password FROM users WHERE name = 'root'

سيصبح:

SELECT password FROM users WHERE name = 0x726f6f74

أو

SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex هو الهروب المثالي.لا توجد طريقة للحقن.

الفرق بين الدالة UNHEX والبادئة 0x

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

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

عرافة () يعمل على أي عمود؛لا داعي للقلق بشأن السلسلة الفارغة.


غالبًا ما تُستخدم الأساليب السداسية كهجمات

لاحظ أن هذه الطريقة السداسية تُستخدم غالبًا كهجوم حقن SQL حيث تكون الأعداد الصحيحة مثل السلاسل تمامًا ويتم الهروب منها فقط mysql_real_escape_string.ثم يمكنك تجنب استخدام علامات الاقتباس.

على سبيل المثال، إذا كنت تفعل شيئًا مثل هذا:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

الهجوم يمكن أن يحقنك بشدة بسهولة.خذ بعين الاعتبار التعليمات البرمجية المحقونة التالية التي تم إرجاعها من البرنامج النصي الخاص بك:

يختار ...حيث معرف = -1 اتحاد الكل حدد اسم الجدول من information_schema.tables

والآن فقط قم باستخراج بنية الجدول:

يختار ...حيث المعرف = -1 اتحاد الكل حدد اسم العمود من information_schema.column حيث اسم الجدول = 0x61727469636c65

وبعد ذلك فقط حدد ما يريده الأشخاص من بيانات.أليس هذا رائعا؟

ولكن إذا قام مبرمج موقع قابل للحقن بتعديله، فلن يكون الحقن ممكنًا لأن الاستعلام سيبدو كما يلي: SELECT ... WHERE id = UNHEX('2d312075...3635')

تحذير مهمل:يستخدم نموذج التعليمات البرمجية لهذه الإجابة (مثل نموذج التعليمات البرمجية للسؤال) لغة PHP MySQL الامتداد، والذي تم إهماله في PHP 5.5.0 وإزالته بالكامل في PHP 7.0.0.

تحذير الأمان:هذه الإجابة لا تتماشى مع أفضل الممارسات الأمنية. الهروب غير كاف لمنع حقن SQL, ، يستخدم البيانات المعدة بدلاً من.استخدم الإستراتيجية الموضحة أدناه على مسؤوليتك الخاصة.(أيضًا، mysql_real_escape_string() تمت إزالته في PHP 7.)

مهم

أفضل طريقة لمنع حقن SQL هي الاستخدام البيانات المعدة بدلا من الهروب, ، مثل الجواب المقبول يوضح.

هناك مكتبات مثل هالة.Sql و إيزي دي بي التي تسمح للمطورين باستخدام البيانات المعدة بشكل أسهل.لمعرفة المزيد حول سبب كون البيانات المعدة أفضل في إيقاف حقن SQL, ، تشير إلى هذا mysql_real_escape_string() تجاوز و تم إصلاح ثغرات أمنية في Unicode SQL حقن في WordPress مؤخرًا.

الوقاية من الحقن - mysql_real_escape_string()

لدى PHP وظيفة مصممة خصيصًا لمنع هذه الهجمات.كل ما عليك فعله هو استخدام وظيفة ما، mysql_real_escape_string.

mysql_real_escape_string يأخذ سلسلة سيتم استخدامها في استعلام MySQL ويعيد نفس السلسلة مع الهروب من جميع محاولات حقن SQL بأمان.في الأساس، سيتم استبدال علامات الاقتباس المزعجة (') التي قد يُدخلها المستخدم ببديل آمن لـ MySQL، وهو اقتباس مُهرب \'.

ملحوظة: يجب أن تكون متصلاً بقاعدة البيانات لاستخدام هذه الوظيفة!

// اتصل بـ MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

يمكنك العثور على مزيد من التفاصيل في MySQL - منع حقن SQL.

تحذير مهمل:يستخدم نموذج التعليمات البرمجية لهذه الإجابة (مثل نموذج التعليمات البرمجية للسؤال) لغة PHP MySQL الامتداد، والذي تم إهماله في PHP 5.5.0 وإزالته بالكامل في PHP 7.0.0.

تحذير الأمان:هذه الإجابة لا تتماشى مع أفضل الممارسات الأمنية. الهروب غير كاف لمنع حقن SQL, ، يستخدم البيانات المعدة بدلاً من.استخدم الإستراتيجية الموضحة أدناه على مسؤوليتك الخاصة.(أيضًا، mysql_real_escape_string() تمت إزالته في PHP 7.)

يمكنك القيام بشيء أساسي مثل هذا:

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

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

مهما كان ما ستستخدمه في النهاية، تأكد من التحقق من أن مدخلاتك لم يتم تشويهها بالفعل magic_quotes أو بعض الأشياء الأخرى ذات النية الحسنة، وإذا لزم الأمر، قم بإدارتها stripslashes أو أي شيء لتطهيره.

تحذير مهمل:يستخدم نموذج التعليمات البرمجية لهذه الإجابة (مثل نموذج التعليمات البرمجية للسؤال) لغة PHP MySQL الامتداد، والذي تم إهماله في PHP 5.5.0 وإزالته بالكامل في PHP 7.0.0.

تحذير الأمان:هذه الإجابة لا تتماشى مع أفضل الممارسات الأمنية. الهروب غير كاف لمنع حقن SQL, ، يستخدم البيانات المعدة بدلاً من.استخدم الإستراتيجية الموضحة أدناه على مسؤوليتك الخاصة.(أيضًا، mysql_real_escape_string() تمت إزالته في PHP 7.)

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

هذه الأمثلة عرضة لحقن SQL:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

أو

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

وفي كلتا الحالتين، لا يمكنك استخدام ' لحماية التغليف.

مصدر: حقن SQL غير المتوقع (عندما لا يكون الهروب كافيًا)

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

اعتماد نمط MVC وإطار عمل مثل CakePHP أو CodeIgniter ربما يكون هو الطريق الصحيح للذهاب:تم حل المهام الشائعة مثل إنشاء استعلامات قاعدة بيانات آمنة وتنفيذها مركزيًا في مثل هذه الأطر.إنها تساعدك على تنظيم تطبيق الويب الخاص بك بطريقة معقولة وتجعلك تفكر في تحميل الكائنات وحفظها أكثر من التفكير في إنشاء استعلامات SQL واحدة بشكل آمن.

أنا أؤيد الإجراءات المخزنة (يتمتع MySQL بدعم الإجراءات المخزنة منذ الإصدار 5.0) من وجهة نظر أمنية - المزايا هي -

  1. معظم قواعد البيانات (بما في ذلك ماي إس كيو إل) تمكين وصول المستخدم إلى أن يقتصر على تنفيذ الإجراءات المخزنة.يعد التحكم الدقيق في الوصول إلى الأمان مفيدًا لمنع تصعيد هجمات الامتيازات.وهذا يمنع التطبيقات المخترقة من القدرة على تشغيل SQL مباشرة على قاعدة البيانات.
  2. يقومون بتجريد استعلام SQL الأولي من التطبيق بحيث تتوفر معلومات أقل حول بنية قاعدة البيانات للتطبيق.وهذا يجعل من الصعب على الأشخاص فهم البنية الأساسية لقاعدة البيانات وتصميم الهجمات المناسبة.
  3. إنهم يقبلون المعلمات فقط، لذا فإن مزايا الاستعلامات ذات المعلمات موجودة.بالطبع - IMO لا تزال بحاجة إلى تحسين مدخلاتك - خاصة إذا كنت تستخدم SQL الديناميكي داخل الإجراء المخزن.

العيوب هي -

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

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

ما هو حقن SQL وكيفية الوقاية منه

دليل PHP لحقن SQL

شرح ميكروسوفت لحقن SQL والوقاية في PHP

وبعض الآخرين مثل منع حقن SQL مع MySQL وPHP

الآن، لماذا تحتاج إلى منع الاستعلام الخاص بك من حقن SQL؟

اود ان اعلمكم:لماذا نحاول منع حقن SQL بمثال قصير أدناه:

الاستعلام عن مطابقة مصادقة تسجيل الدخول:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

الآن، إذا قام شخص ما (المتسلل) بوضع

$_POST['email']= admin@emali.com' OR '1=1

وكلمة المرور أي شيء....

سيتم تحليل الاستعلام في النظام فقط حتى:

$query="select * from users where email='admin@emali.com' OR '1=1';

سيتم التخلص من الجزء الآخر.إذن، ماذا سيحدث؟سيتمكن المستخدم غير المصرح له (المتسلل) من تسجيل الدخول كمسؤول دون الحصول على كلمة المرور الخاصة به.الآن، يمكنه فعل أي شيء يمكن أن يفعله المسؤول/البريد الإلكتروني.انظر، إنه أمر خطير جدًا إذا لم يتم منع حقن SQL.

أعتقد أنه إذا أراد شخص ما استخدام PHP وMySQL أو أي خادم قاعدة بيانات آخر:

  1. فكر في التعلم شركة تنمية نفط عمان (كائنات بيانات PHP) - إنها طبقة وصول إلى قاعدة البيانات توفر طريقة موحدة للوصول إلى قواعد بيانات متعددة.
  2. فكر في التعلم MySQLi
  3. استخدم وظائف PHP الأصلية مثل: strip_tags, mysql_real_escape_string أو إذا كان متغير رقمي، فقط (int)$foo.اقرأ المزيد عن أنواع المتغيرات في PHP هنا.إذا كنت تستخدم مكتبات مثل PDO أو MySQLi، فاستخدم دائمًا شركة تنمية نفط عمان::quote() و mysqli_real_escape_string().

أمثلة المكتبات:

---- شركة تنمية نفط عمان

----- لا توجد عناصر نائبة - جاهز لإدخال SQL! انها سيئة

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- العناصر النائبة غير المسماة

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- العناصر النائبة المسماة

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

ملاحظة:

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

ولكن في حين أن كل من PDO و MySqli سريعان للغاية ، فإن MySqli يؤدي بشكل أسرع بشكل لا معنى له في المعايير-حوالي 2.5 ٪ للبيانات غير المستعدة ، و ~ 6.5 ٪ للآثار المحضرة.

ويرجى اختبار كل استعلام في قاعدة البيانات الخاصة بك - إنها طريقة أفضل لمنع الحقن.

إذا أمكن، قم بإلقاء أنواع المعلمات الخاصة بك.ولكنها تعمل فقط على أنواع بسيطة مثل int وbool وfloat.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

إذا كنت ترغب في الاستفادة من محركات ذاكرة التخزين المؤقت، مثل ريديس أو ميمكاشد, ربما يكون DALMP خيارًا.ويستخدم نقيا MySQLi.افحص هذا: طبقة تجريد قاعدة بيانات DALMP لـ MySQL باستخدام PHP.

يمكنك أيضًا "إعداد" الوسائط الخاصة بك قبل إعداد الاستعلام الخاص بك حتى تتمكن من إنشاء استعلامات ديناميكية وفي النهاية يكون لديك استعلام بيانات مُجهز بالكامل. طبقة تجريد قاعدة بيانات DALMP لـ MySQL باستخدام PHP.

بالنسبة لأولئك غير متأكدين من كيفية استخدام PDO (القادمة من mysql_ وظائف)، لقد تقدمت غلاف PDO بسيط جدًا جدًا هذا ملف واحد.إنه موجود لإظهار مدى سهولة القيام بكل الأشياء الشائعة التي تحتاج التطبيقات إلى القيام بها.يعمل مع PostgreSQL، وMySQL، وSQLite.

في الأساس، اقرأها أثناء قراءة الدليل لمعرفة كيفية استخدام وظائف PDO في الحياة الواقعية لتسهيل تخزين واسترجاع القيم بالتنسيق أنت يريد.

أريد عمودًا واحدًا

$count = DB::column('SELECT COUNT(*) FROM `user`);

أريد نتائج مصفوفة (مفتاح => قيمة) (أيلإنشاء مربع اختيار)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

أريد نتيجة صف واحد

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

أريد مجموعة من النتائج

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

باستخدام هذه الوظيفة PHP mysql_escape_string() يمكنك الحصول على وقاية جيدة بطريقة سريعة.

على سبيل المثال:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - الهروب من سلسلة لاستخدامها في mysql_query

ولمزيد من الوقاية يمكنك إضافة في النهاية ...

wHERE 1=1   or  LIMIT 1

وأخيرا تحصل على:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

بعض الإرشادات للهروب من الأحرف الخاصة في عبارات SQL.

لا تستخدم ماي إس كيو إل, ، تم إهمال هذا الامتداد، استخدمه MySQLi أو شركة تنمية نفط عمان.

MySQLi

للهروب يدويًا من الأحرف الخاصة في سلسلة، يمكنك استخدام mysqli_real_escape_string وظيفة.لن تعمل الوظيفة بشكل صحيح ما لم يتم تعيين مجموعة الأحرف الصحيحة mysqli_set_charset.

مثال:

$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

للهروب التلقائي من القيم مع البيانات المعدة، استخدم mysqli_prepare, ، و mysqli_stmt_bind_param حيث يجب توفير أنواع متغيرات الربط المقابلة لإجراء التحويل المناسب:

مثال:

$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

بغض النظر عما إذا كنت تستخدم عبارات معدة أو mysqli_real_escape_string، فيجب عليك دائمًا معرفة نوع بيانات الإدخال التي تعمل معها.

لذا، إذا كنت تستخدم عبارة معدة، فيجب عليك تحديد أنواع المتغيرات لوظيفة mysqli_stmt_bind_param.

واستخدام mysqli_real_escape_string مخصص، كما يقول الاسم، للتهرب من الأحرف الخاصة في سلسلة، لذلك لن يجعل الأعداد الصحيحة آمنة.الغرض من هذه الوظيفة هو منع انقطاع السلاسل في عبارات SQL والأضرار التي قد تسببها لقاعدة البيانات.تعتبر mysqli_real_escape_string دالة مفيدة عند استخدامها بشكل صحيح، خاصة عند دمجها مع sprintf.

مثال:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

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

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

سيؤدي هذا إلى تقييد المستخدم بحيث يقتصر على الاستعلام المحدد فقط.قم بإزالة إذن الحذف وبالتالي لن يتم حذف البيانات أبدًا من الاستعلام الذي تم إطلاقه من صفحة PHP.الشيء الثاني الذي يجب فعله هو مسح الامتيازات حتى يقوم MySQL بتحديث الأذونات والتحديثات.

FLUSH PRIVILEGES; 

مزيد من المعلومات حول دافق.

لرؤية الامتيازات الحالية للمستخدم، أطلق الاستعلام التالي.

select * from mysql.user where User='username';

تعلم المزيد عن منحة.

تحذير الأمان:هذه الإجابة لا تتماشى مع أفضل الممارسات الأمنية. الهروب غير كاف لمنع حقن SQL, ، يستخدم البيانات المعدة بدلاً من.استخدم الإستراتيجية الموضحة أدناه على مسؤوليتك الخاصة.(أيضًا، mysql_real_escape_string() تمت إزالته في PHP 7.)

تحذير مهمل:تم إهمال ملحق MySQL في هذا الوقت.نوصي باستخدام تمديد شركة تنمية نفط عمان

أستخدم ثلاث طرق مختلفة لمنع تطبيق الويب الخاص بي من التعرض لحقن SQL.

  1. استخدام mysql_real_escape_string(), ، وهي وظيفة محددة مسبقًا في بي أتش بي, ، ويضيف هذا الرمز خطوطًا مائلة عكسية إلى الأحرف التالية: \x00, \n, \r, \, ', " و \x1a.قم بتمرير قيم الإدخال كمعلمات لتقليل فرصة إدخال SQL.
  2. الطريقة الأكثر تقدمًا هي استخدام PDOs.

آمل أن يكون هذا سيساعدك.

خذ بعين الاعتبار الاستعلام التالي:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

لن يحمي mysql_real_escape_string() هنا.إذا كنت تستخدم علامات الاقتباس المفردة (' ') حول المتغيرات الخاصة بك داخل استعلامك فهذا ما يحميك من ذلك.إليك الحل أدناه لهذا:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

هذا سؤال لديه بعض الإجابات الجيدة حول هذا الموضوع.

أقترح أن استخدام PDO هو الخيار الأفضل.

يحرر:

mysql_real_escape_string() تم إهماله اعتبارًا من PHP 5.5.0.استخدم إما mysqli أو PDO.

البديل لـ mysql_real_escape_string() هو

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

مثال:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

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

الآن، هدفنا هو منع التهديدات الأمنية مثل هجمات حقن SQL، والسؤال الذي يطرح نفسه (كيفية منع هجوم حقن SQL باستخدام PHP)، كن أكثر واقعية، وتصفية البيانات أو مسح بيانات الإدخال هي الحالة عند استخدام بيانات إدخال المستخدم داخل مثل هذه استعلام، استخدام PHP أو أي لغة برمجة أخرى ليس هو الحال، أو كما أوصى المزيد من الأشخاص باستخدام التكنولوجيا الحديثة مثل البيان المعد أو أي أدوات أخرى تدعم حاليًا منع حقن SQL، هل تعتبر أن هذه الأدوات لم تعد متوفرة بعد الآن؟كيف يمكنك تأمين التطبيق الخاص بك؟

نهجي ضد حقن SQL هو:مسح بيانات مدخلات المستخدم قبل إرسالها إلى قاعدة البيانات (قبل استخدامها داخل أي استعلام).

تصفية البيانات لـ (تحويل البيانات غير الآمنة إلى بيانات آمنة)اعتبر ذلك شركة تنمية نفط عمان و MySQLi غير متوفر، كيف يمكنك تأمين طلبك؟هل تجبرني على استخدامها؟ماذا عن اللغات الأخرى غير PHP؟أفضل تقديم أفكار عامة حيث يمكن استخدامها لحدود أوسع وليس فقط للغة معينة.

  1. مستخدم SQL (الحد من امتيازات المستخدم):عمليات SQL الأكثر شيوعًا هي (SELECT، UPDATE، INSERT)، فلماذا يتم منح امتياز UPDATE لمستخدم لا يحتاج إليه؟على سبيل المثال تسجيل الدخول، وصفحات البحث تستخدم SELECT فقط، فلماذا تستخدم مستخدمي قاعدة البيانات في هذه الصفحات بامتيازات عالية؟قاعدة:لا تقم بإنشاء مستخدم قاعدة بيانات واحد لجميع الامتيازات، لجميع عمليات SQL، يمكنك إنشاء مخططك مثل (deluser، Selectuser، Updateuser) كأسماء مستخدمين لسهولة الاستخدام.

يرى مبدأ الامتياز الأقل

  1. تصفية البيانات:قبل إنشاء أي استعلام، يجب التحقق من صحة مدخلات المستخدم وتصفيتها، بالنسبة للمبرمجين، من المهم تحديد بعض الخصائص لكل متغيرات إدخال المستخدم:نوع البيانات ونمط البيانات وطول البيانات.يجب التحقق من صحة الحقل الذي يمثل رقمًا بين (x وy) تمامًا باستخدام القاعدة الدقيقة، بالنسبة للحقل الذي يمثل سلسلة (نص):النمط هو الحال، على سبيل المثال، يجب أن يحتوي اسم المستخدم على بعض الأحرف فقط لنفترض [a-zA-Z0-9_-.] ويختلف الطول بين (x و n) حيث x و n (أعداد صحيحة، x <=n ).قاعدة:يعد إنشاء عوامل تصفية وقواعد تحقق دقيقة من أفضل الممارسات بالنسبة لي.

  2. استخدم أدوات أخرى:هنا، سأتفق معك أيضًا على أن البيان المجهز (الاستعلام المعلمي) والإجراءات المخزنة، عيوب هذه الطرق هي أن هذه الطرق تتطلب مهارات متقدمة لا توجد لدى معظم المستخدمين، الفكرة الأساسية هنا هي التمييز بين استعلام SQL والبيانات التي يتم استخدامها في الداخل، يمكن استخدام كلا الطريقتين حتى مع البيانات غير الآمنة، لأن بيانات إدخال المستخدم هنا لا تضيف أي شيء إلى الاستعلام الأصلي مثل (any أو x=x).لمزيد من المعلومات، يرجى القراءة ورقة الغش الخاصة بمنع حقن OWASP SQL.

الآن، إذا كنت مستخدمًا متقدمًا، فابدأ في استخدام هذا الدفاع كما تريد، ولكن بالنسبة للمبتدئين، إذا لم يتمكنوا من تنفيذ الإجراء المخزن وإعداد البيان بسرعة، فمن الأفضل تصفية بيانات الإدخال قدر الإمكان.

أخيرًا، لنفترض أن المستخدم يرسل هذا النص أدناه بدلاً من إدخال اسم المستخدم الخاص به:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

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

النقطة الأخيرة هي اكتشاف السلوك غير المتوقع الذي يتطلب المزيد من الجهد والتعقيد؛لا ينصح به لتطبيقات الويب العادية.السلوك غير المتوقع في إدخال المستخدم أعلاه هو SELECT، UNION، IF، SUBSTRING، BENCHMARK، SHA، root بمجرد اكتشاف هذه الكلمات، يمكنك تجنب الإدخال.

التحديث 1:

علق أحد المستخدمين بأن هذا المنشور عديم الفائدة، حسنًا!هنا هو ما OWASP.ORG متاح:

الدفاعات الأولية:

الخيار 1:استخدام البيانات المعدة (الاستعلامات ذات المعلمات)
الخيار 2:استخدام الإجراءات المخزنة
الخيار رقم 3:الهروب من كافة المدخلات المقدمة من قبل المستخدم

دفاعات إضافية:

فرض أيضًا:الامتياز الأقل
نفذ أيضًا:التحقق من صحة إدخال القائمة البيضاء

كما تعلم، يجب أن تكون المطالبة بالمقالة مدعومة بحجة صحيحة، أو بمرجع واحد على الأقل!وإلا فهو اعتداء وادعاء سيء!

التحديث2:

من دليل PHP، بي أتش بي:البيانات المعدة - دليل:

الهروب وحقن SQL

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

يعتبر الهروب التلقائي للقيم داخل الخادم ميزة أمان لمنع حقن SQL.يمكن تحقيق نفس درجة الأمان من خلال عبارات غير مستعدة إذا تم هروب قيم الإدخال بشكل صحيح.

التحديث3:

لقد قمت بإنشاء حالات اختبار لمعرفة كيفية قيام PDO وMySQLi بإرسال الاستعلام إلى خادم MySQL عند استخدام العبارة المعدة:

الهدف المحمي:

$user = "''1''"; //Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

سجل الاستعلام:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

ماي أسكلي:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

سجل الاستعلام:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

من الواضح أن البيان المعد يفلت أيضًا من البيانات، ولا شيء آخر.

كما ذكرنا في البيان أعلاه The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly, وبالتالي فإن هذا يثبت أن التحقق من صحة البيانات مثل intval() تعتبر فكرة جيدة للقيم الصحيحة قبل إرسال أي استعلام، بالإضافة إلى منع بيانات المستخدم الضارة قبل إرسال الاستعلام النهج الصحيح والصحيح.

يرجى الاطلاع على هذا السؤال لمزيد من التفاصيل: ترسل شركة PDO استعلامًا أوليًا إلى MySQL بينما يرسل Mysqli استعلامًا مُجهزًا، وكلاهما ينتج نفس النتيجة

مراجع:

  1. ورقة الغش لحقن SQL
  2. حقن SQL
  3. أمن المعلومات
  4. مبادئ الأمن
  5. تأكيد صحة البيانات

هناك طريقة بسيطة تتمثل في استخدام إطار عمل PHP مثل CodeIgniter أو لارافيل التي تحتوي على ميزات مدمجة مثل التصفية والسجل النشط بحيث لا داعي للقلق بشأن هذه الفروق الدقيقة.

** تحذير:لا ينطبق النهج الموضح في هذه الإجابة إلا على سيناريوهات محددة للغاية وغير آمن نظرًا لأن هجمات حقن SQL لا تعتمد فقط على القدرة على الحقن X=Y.**

إذا كان المهاجمون يحاولون اختراق النموذج عبر PHP $_GET متغير أو باستخدام سلسلة استعلام عنوان URL، ستتمكن من التقاطها إذا لم تكن آمنة.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

لأن 1=1, 2=2, 1=2, 2=1, 1+1=2, ، إلخ...هي الأسئلة الشائعة لقاعدة بيانات SQL للمهاجم.ربما يتم استخدامه أيضًا بواسطة العديد من تطبيقات القرصنة.

ولكن يجب أن تكون حذرًا، حيث لا يجب عليك إعادة كتابة استعلام آمن من موقعك.يمنحك الكود أعلاه نصيحة لإعادة الكتابة أو إعادة التوجيه (انه يعتمد عليك) سلسلة الاستعلام الديناميكية الخاصة بالقرصنة هذه في صفحة ستخزن معلومات المهاجم عنوان IP, ، أو حتى ملفات تعريف الارتباط الخاصة بهم، أو السجل، أو المتصفح، أو أي معلومات حساسة أخرى، حتى تتمكن من التعامل معهم لاحقًا عن طريق حظر حسابهم أو الاتصال بالسلطات.

هناك الكثير من الإجابات ل بي إتش بي وماي إس كيو إل, ، ولكن هنا رمز لـ PHP وأوراكل لمنع حقن SQL وكذلك الاستخدام المنتظم لبرامج تشغيل oci8:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

فكرة جيدة هي استخدام "مخطط الكائنات العلائقية" يحب لغة اصطلاحية:

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

إنه لا يوفر عليك فقط من حقن SQL ولكن من أخطاء بناء الجملة أيضًا!يدعم أيضًا مجموعات من النماذج مع تسلسل الطرق لتصفية أو تطبيق الإجراءات على نتائج متعددة في وقت واحد واتصالات متعددة.

تحذير مهمل:يستخدم نموذج التعليمات البرمجية لهذه الإجابة (مثل نموذج التعليمات البرمجية للسؤال) لغة PHP MySQL الامتداد، والذي تم إهماله في PHP 5.5.0 وإزالته بالكامل في PHP 7.0.0.

تحذير الأمان:هذه الإجابة لا تتماشى مع أفضل الممارسات الأمنية. الهروب غير كاف لمنع حقن SQL, ، يستخدم البيانات المعدة بدلاً من.استخدم الإستراتيجية الموضحة أدناه على مسؤوليتك الخاصة.(أيضًا، mysql_real_escape_string() تمت إزالته في PHP 7.)

استخدام شركة تنمية نفط عمان و ماي أسكلي تعتبر ممارسة جيدة لمنع حقن SQL، ولكن إذا كنت تريد حقًا العمل مع وظائف واستعلامات MySQL، فمن الأفضل استخدامها

mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

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

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

ومن الأفضل استخدام هذه الوظائف للتحقق من بيانات الإدخال mysql_real_escape_string.

لقد كتبت هذه الوظيفة الصغيرة منذ عدة سنوات:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

يسمح هذا بتشغيل البيانات في سلسلة C#-ish ذات سطر واحد. التنسيق مثل:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

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

التحديث الأمني:السابق str_replace يسمح الإصدار بالحقن عن طريق إضافة {#} من الرموز المميزة إلى بيانات المستخدم.هذا preg_replace_callback الإصدار لا يسبب مشاكل إذا كان البديل يحتوي على هذه الرموز المميزة.

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