حل ثغره امنيه في كود PHP

zeyad_mohamed1

انا انشئت موقع ويب بواسطة php وكتبت داله تسجيل الدخول بهذه الطريقه

<?php

$db_connection = mysqli_connect("localhost", "zeyad", "12345678", "trip");

$username = $_POST['username'];
$password = $_POST['password'];


$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($db_connection, $query);


if (mysqli_num_rows($result) == 1) {
 
    session_start();
    $_SESSION['username'] = $username;
    header('Location: dashboard.php');
} else {
   
     header('Location: errorPage.php');
}
mysqli_close($db_connection);
?>


ولاكن موقعي تم اختراقه كيف احل المشكله و ماهي الثغره اريد شرح ارجوكم

يرجى الدخول لحسابك أو تسجيل حساب لتستطيع إضافة تعليق
حساب جديد دخول

التعليقات

يوجد مشكله خطيره بكتابة الكود تم اختراق موقعك عن طريق ثغره معروفه باسم SQL Injection

اولا لنعرف ما هو حقن Sql

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

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

يمكنك اتباح هذه التعليمات لحل المشكله

استخدام Prepared Statements

مفهوم الـ prepared statement هو أنه يتم إعداد الاستعلام SQL مسبقًا وتضمين معلمات (parameters) في الاستعلام بدلاً من إدراج القيم المدخلة مباشرة. هذا يمنع المهاجم من إدخال تعليمات SQL ضارة.

بدلاً من كتابة الاستعلام SQL مباشرة كما كان مع الثغرة، سنقوم بتحضير الاستعلام مع معلمات ومن ثم تزويد القيم المدخلة من قبل المستخدم. على سبيل المثال:

<?php
// اتصال بقاعدة البيانات
$db_connection = mysqli_connect("localhost", "username", "password", "database");


// معلومات تسجيل الدخول من النموذج
$username = $_POST['username'];
$password = $_POST['password'];


// استعلام مع معلمات
$query = "SELECT * FROM users WHERE username=? AND password=?";
$stmt = mysqli_prepare($db_connection, $query);


// تزويد القيم المدخلة كمعلمات
mysqli_stmt_bind_param($stmt, "ss", $username, $password);


// تنفيذ الاستعلام
mysqli_stmt_execute($stmt);


// استخراج البيانات
$result = mysqli_stmt_get_result($stmt);


// التحقق من صحة معلومات تسجيل الدخول
if (mysqli_num_rows($result) == 1) {
    // تسجيل الدخول ناجح
    session_start();
    $_SESSION['username'] = $username;
    header('Location: dashboard.php');
} else {
    // فشل تسجيل الدخول
    echo "خطأ: اسم المستخدم أو كلمة المرور غير صحيحة.";
}


// إغلاق اتصال قاعدة البيانات
mysqli_close($db_connection);
?>


ما تم فعله هنا هو استخدام mysqli_prepare() لإعداد الاستعلام مع معلمات ? ومن ثم تزويد القيم المدخلة باستخدام mysqli_stmt_bind_param(). هذا يجعل الاستعلام آمنًا ويمنع هجمات SQL Injection.

هذا هو حلا لمشكلة SQL Injection.

الثغرة الأمنية في الكود هي خاصة بالحقن SQL (SQL Injection) حيث تسمح الثغرة للمهاجمين بإدخال تعليمات SQL عشوائية من خلال حقل اسم المستخدم أو كلمة المرور، مما قد يؤدي إلى:

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

سبب الثغرة عدم استخدام التعقيم (Sanitization) لبيانات الإدخال قبل استخدامها في استعلامات SQL.

لذا، استخدم وظائف PHP المدمجة مثل mysqli_real_escape_string() لتعقيم بيانات الإدخال قبل استخدامها في استعلامات SQL.

كالتالي:

$username = mysqli_real_escape_string($db_connection, $_POST['username']);
$password = mysqli_real_escape_string($db_connection, $_POST['password']);

أيضًا استخدام بيانات مُعدّة (prepared statements) بدلاً من دمج بيانات الإدخال مباشرةً في استعلامات SQL.

$stmt = $db_connection->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();

ومن الأفضل الإعتماد على مكتبات مُخصصة لحماية تطبيقات PHP من هجمات حقن SQL، مثل مكتبة PDO.

حاول استخدام PDO (PHP Data Objects) بدلاً من mysqli أو mysql لتحقيق طبقة أكثر أمانًا وقوة في التعامل مع قواعد البيانات.يتيح PDO استخدام تعبيرات استعلام بشكل أسهل.مثل

$db_connection = new PDO("mysql:host=localhost;dbname=trip", "zeyad", "12345678");
$query = "SELECT * FROM users WHERE username=:username AND password=:password";
$stmt = $db_connection->prepare($query);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();
$result = $stmt->fetchAll();

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

على سبيل المثال يمكنني استغلال ثغرة SQL injection في حقل اسم المستخدم username في كودك البرمجي بإدخال قيمة معينة في هذا الحقل (لاحظ كيف كتبت كود sql في الحقل بحيث يجعل الشرط محقق دومًا) 

' OR '1'='1'; --

أي إذا قمت بإدخال هذه القيمة في حقل اسم المستخدم، سيتشكل استعلام SQL بالشكل التالي:

SELECT * FROM users WHERE username='' OR '1'='1'; --' AND password='';

التعبير OR '1'='1' دائمًا محقق true وسيتسبب في إرجاع جميع سجلات قاعدة البيانات، وبالتالي سيتجاوز أي فحص على كلمة المرور فالرمز -- يتيح إيقاف أي جزء من الاستعلام قد يؤثر على النتيجة.

وهناك عدة أساليب لتصحيح هذه المشكلة وتجنب هذا النوع من الهجمات من بينها استخدام الاستعلامات بالبارامترات parameterized queries أو العبارات المُجهزة prepared statements من أجل معاملة القيم التي يدخلها المستخدم على أنها بيانات ولا تسمح بمعاملتها ككود sql أي عليك فصل بنية الاستعلام عن القيم التي يوفرها المستخدم لمنعه من حقن التعليمات البرمجية الخبيثة وتعزيز الأمان.

إليك مثال على استخدام الطريقة prepared statements لتحسين أمان الكود:

<?php
$db_connection = mysqli_connect("localhost", "zeyad", "12345678", "trip");
$username = $_POST['username'];
$password = $_POST['password'];
// أسلوب prepared statements لاحظ استخدام ? مكان بيانات المستخدم
$stmt = mysqli_prepare($db_connection, "SELECT * FROM users WHERE username=? AND password=?");
// هنا يمكنك ربط قيم البيانات التي يوفرها المستخدم وفصل الكود عن البيانات
mysqli_stmt_bind_param($stmt, "ss", $username, $password);
// تنفيذ العبارة 
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
if (mysqli_num_rows($result) == 1) {
 session_start();
 $_SESSION['username'] = $username;
 header('Location: dashboard.php');
} else {
 header('Location: errorPage.php');
}
mysqli_stmt_close($stmt);
mysqli_close($db_connection);
?>

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

ويمكنك مطالعة هذا المقال لمزيد من التفاصيل حول هذه الثغرة.