مدونة عبدالمحسن المنصور

مبرمج، أعمل في الادارة العامة للطيران المدنى.

ACoders @ Twitter

كيف تحمي برمجيتك من حقن قواعد البيانات

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

ماذا يعني حقن قواعد البيانات؟

حقن قواعد البيانات بشكل مُبسط تعني بان يتم حقن (السؤال "Query") لقاعدة البيانات في بيانات تقوم بتغير شكل السؤال، وبسبب ذلك فأنه يستطيع المخرب حصول على بيانات او الدخول بعضويات من دون الرقم السري وكمثال شاهد (المصدر"Source Code") التالي:

$sql = "SELECT 
           * 
        FROM 
           users 
        WHERE 
           user      = '".$_POST['user']."' 
        AND 
           password  = '".md5( $_POST['password'] )."' 
";
$result = mysqli_query($link, $sql);

لو تلاحظ مُتغير $sql يقبل مُتغيرات تُأخذ بشكل مباشر من المتصفح، وبتغير متغير $_POST['user'] الي مثلا 'admin'; # فسيتم تسجيل الدخول بعضويه admin من دون الحاجة للرقم السري، و ذلك لأنه المصدر السابق يسمح بحقن البيانات من المتصفح بشكل مباشر، و ذلك بسبب أنه لم يتم التحقق من الُمدخلات.

مع التحديث الجديد لقواعد MySQL تم إضافة خيار جديد باسم (عبارات معدة "prepared statements")، بحيث يتم تجهيز السؤال في قاعدة البيانات من دون حاجة لكتابة المُدخلات في نفس السؤال الرئيسي و بذلك لن يستطيع اي شخص من حقن قاعدة البيانات.

طريقة استخدام العبارات المعدة في مكتبة PDO

اذا كنت تستخدم مكتبة PDO، شاهد المصدر التالي:

$user = 'test'; //-- اسم المستخدم لقاعدة البيانات
$pass = 'pass'; //-- الرقم السري لقاعدة البيانات
$db   = 'dbName'; //-- اسم قاعدة البيانات

try
{
    $pdo = new PDO( 'mysql:host=localhost;dbname=' . $db, $user, $pass );

    $sql = "SELECT * FROM topics WHERE id = :id";

    $stmt = $pdo->prepare( $sql );

    if ( !( $id = filter_input( INPUT_POST, 'id', FILTER_VALIDATE_INT ) ) )
    {
        die( 'no vaild id was given' );
    };

    $stmt->execute( [ 'id' => $id ] );

    $info = $stmt->fetchAll();

    var_dump( $info );
}
catch ( \PDOException $e )
{
    die( $e->getMessage() );
}

لو تلاحظ لقد تم استبدال المُدخلات في السؤال بي :id، والهدف من ذلك هو أن يتم إرسال السؤال لقاعدة البيانات اولا ومن ثم يتم إرسال المُدخلات، عن طريق المصدر التالي

if ( !( $id = filter_input( INPUT_POST, 'id', FILTER_VALIDATE_INT ) ) )
{
    die( 'no vaild id was given' );
};
$stmt->execute( [ 'id' => $id ] );

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

طريقة استخدام العبارات المعدة في مكتبة MySQLi

يفضل كثير من المبرمجين استخدام هذه المكتبة، و انا عن نفسي لا استخدمها بل أستخدم PDO ولكي تستخدم العبارات المعدة في مكتبة MySQLi شاهد المصدر التالي

$user   = 'test'; //-- اسم المستخدم لقاعدة البيانات
$pass   = 'pass'; //-- الرقم السري لقاعدة البيانات
$db     = 'dbName'; //-- اسم قاعدة البيانات
$dbhost = 'localhost'; //-- خادم قواعد البياناا

$mysqli = new mysqli( $dbhost, $user, $pass, $db );

if ( $mysqli->connect_error )
{
    die( 'Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error );
}

$stmt = $mysqli->prepare( "SELECT * FROM topics WHERE id = ?" );

if ( !( $id = filter_input( INPUT_POST, 'id', FILTER_VALIDATE_INT ) ) )
{
    die( 'no vaild id was given' );
};

$stmt->bind_param( 'i', $id );

if ( !$stmt->execute() )
{
    echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}

لو تلاحظ يختلف المصدر قليلاً عن مكتبة PDO لأنه في مكتبة MySQLi يجب عليك تحديد نوع المدخلات (احرف "s"، ارقام "i"، باينري "b")،

لمزيد من المعلومات حول موضوع (الأسئلة المُعدة "Prepared Statements")، أضغط هنا لمكتبة PDO، أو أضغط هنا لمكتبة MySQLi

التعليقات