در حال ارتباط با سرور...




لطفا نظرات و پيشنهادات خود را بمنظور ارتقاء کيفي هرچه بيشتر سايت با ما در ميان بگذاريد.

ویدیوها مقالات کتاب ها اخبار پرسش و پاسخ
برنامه‌های رومیزی مبتنی بر وب زبان های برنامه سازی پایگاه داده سیستم عامل شبکه 
مقاله بگذارید
توسط : hamedkh دسته بندی : مبتنی بر وب تاریخ : 1391-12-19 20:50:50

 

پس از آنکه در مقاله آموزشی قبل مطالبی را در مورد حملات و خطرات سمت کلاینت مطرح کردیم، امروز می خواهیم به سراغ یکی از خطرناکترین و رایجترین نوع حملات سمت سرور برویم. حتما با واژه SQL Injection آشنایی دارید. SQL Injection یا بعبارتی تزریق کد های SQL بداخل کوئری های SQL  نوعی از حملات رایج در دنیای وب است که مهاجمان از طریق وارد کردن دستورات SQL بداخل برنامه، قصد در یافتن Schema و اطلاعات حیاتی پایگاه داده ما را دارند. می توان گفت هنگامی برنامه شما در مقابل حملات SQL Injection آسیب پذیر می شود که داده های ورودی کاربر را بدون هیچگونه سنجش اعتباری در پرس و جو های برنامه خود استفاده نمایید.

برای نمونه به تکه کد زیر توجه کنید.

 

<?php
// warning, dangerous code
Yii::app()->db
    ->createCommand("DELETE FROM mytable WHERE id = " . $_GET['id'])
    ->execute();
$comments = Comment::model->findAll("user_id = " . $_GET['id']);

همانطور که می بینید در این تکه کد پارامتر id  مستقیما از طریق GET گرفته شده و بدون هیچ بررسی ای در داخل کوئری مورد استفاده قرار گرفته است. حال فرض کنید کاربر با دستکاری در url یا ارسال پارامتر از طریق کنسول مرور گر بجای id رشته “4 OR 1=1” را ارسال کند. در این صورت او خواهد توانست علاوه بر کامنت #4 تمامی کامنت های دیگر را نیز براحتی پاک کند. اما در مورد درخواست دوم مهاجم قادر خواهد بود با وارد کردن رشته “2 UNION SELECT … “ تمامی محتویات پایگاه داده را ببیند.

 

راه های مقابله با SQL Injection:

در Yii Framework می توان بجای استفاده از کد های SQL ار سینتکس PHP برای ساخت و اجرای یک کوئری استفاده نمود.برای مثال تکه کد مثال قبل را می توان به این صورت امن تر نمود :

 

<?php
// still lacks validation (see "Validating user input" above), but more secure
MyModel::model()->findByPk($_GET['id'])->delete();
// uses validation with a type cast
$comments = Comment::model->findAllByAttributes(array('user_id' => (int)$_GET['id']);

بعنوان یک اصل همواره بیاد داشته باشید که اگر یک شرط SQL را تنها بصورت یک رشته متنی خالص بنویسید امنیت پایینتری را نسبت به روش های نوشتن آن با استفاده از PHP خواهید داشت. همچنین بیاد داشته باشید که بهتر در هنگام ارسال پارامتر ها بداخل عبارات SQL بیشتر از پارامتر های آرایه ای استفاده نمایید.مثال :

 

<?php
// warning: potential sql injection
$comments = Comment::model->findAll("post_id = $postId AND author_id IN (" . join(',', $ids) . ")");
// secure (note how an array value is gives a "IN" since Yii
$comments = Comment::model->findAllByAttributes(array("post_id" => $postId, "author_id" => $ids));

عبارات آماده شده (prepared statements):

با وجود امکاناتی که Yii Framework در اختیار ما قرار می دهد هنوز هم در برخی از کاربرد ها مجبور هستیم مستقیما و بدون واسطه از عبارات SQL در برنامه خود استفاده کنیم. بعنوان نمونه تکه کد زیر را در نظر بگیرید که در آن از دو متغیر استفاده کرده ایم :

 

SELECT CONCAT(prefix, title) AS title, author_id, post_id, submit_date
  FROM t_comment
  WHERE (date > '{$date}' OR date IS NULL) AND title LIKE '%{$text}%'

همانطور که گفته شد استفاده از این عبارت SQL در برنامه مخاطره آمیز می باشد و باید از آن دوری نمود. اما برای ایمن نمودن آن چه کار می توان انجام داد. دو روش برای این منظور پیشنهاد شده است :

  1. اینکه پارامتر ها را فیلتر (escape) کنیم
  2. یا آنکه از عبارات آماده شده (prepared statements)  استفاده کنیم.

در صورتی که بخواهید از راه حل اول یعنی فیلتر کردن استفاده نمایید، می توانید از متد CDbConnction::quoteValue() استفاده کنید. برای مثال بجای عبارت “date >’ {$date}’” از “date > “ . Yii::app()->db->quoteValue($date)  استفاده کنید.

دومین راهکار که بهتر و مطمئن تر میباشد و توصیه بیشتری به آن شده است استفاده از عبارات آماده شده می باشد که به شما امکان می دهد در داخل کد SQL خود پارامتر هایی را تعریف کنید و این پارامتر ها را به مقادیری مقید (bind) کرده تا سرور در زمان اجرا پارامتر ها را با مقادیر مورد نظر آنها مقدار دهی نماید و کوئری را اجرا نماید. به این ترتیب خطر SQL Injection از بین خواهد رفت و کد شما به کدی امن مبدل می شود (البته تنها از نقطه نظر Injection).

حال ببینیم چگونه می توان این راهکار را در Yii پیاده سازی کرد. برای این کار دو روش ارائه می شود :

 

<?php
// Note the parameters are written :param without surrounding quotes
$sql = "SELECT CONCAT(prefix, title) AS title, author_id, post_id, date "
    . "FROM t_comment "
    . "WHERE (date > :date OR date IS NULL) AND title LIKE :text"
 
// 1st way, using explicit binds
$command = Yii::app()->db->createCommand($sql);
$command->bindParam(":date", $date, PDO::PARAM_STR);
$command->bindParam(":text", "%{$text}%", PDO::PARAM_STR);
$results = $command->execute();
 
// second way
$command = Yii::app()->db->createCommand($sql);
$results = $command->execute(array(':date' => $date, ':text' => "%{$text}%"));

در روش اول ما انقیاد صریح را داریم که کمی پیچیده تر نسبت به روش دوم بنظر می رسد اما این مزیت را  دارد که در ان قادر هستیم نوع پارامتر مقید شده را نیز تعیین نماییم. برای خواندن داده ها از پایگاه داده نیز می توان از این روش استفاده کرد. برای این کار بصورت زیر می توان عمل نمود:

 

<?php
$comments = Comment::model->findAllBySql($sql, array(':date' => $date, ':text' => "%{$text}%"));

اگر هنوز با کارکردن با پایگاه داده در Yii راحت نیستید پیشنهاد می شود سری به بخش مربوط به کار با پایگاه داده در Yii Framework بزنید.

 

شرط های LIKE :

نکته ای که در این بخش می خواهم توجه شما را به آن جلب کنم رفتار ویژه تابع LIKE در SQL نسبت به کاراکتر های _ و %  است. بطور معمول این رفتار مشکل و خطری را برای ما بدنبال ندارد اما چنانچه ما پرس و جوی بزرگی داشته باشیم و بخواهیم شرطی مانند “begin%” را به “%s%a%” تبدیل کنیم، این کار باعث سنگین شدن بیش از حد پرس و جو شده و سرور SQL را بشدت کند می نماید چراکه در این پرس و جو هیچگونه ایندکسی مورد استفاده قرار نمیگیرد. بهمین دلیل باید در مواقعی که ورودی کاربر را در یک شرط LIKE قرار می دهید کاملا بررسی کنید و مراقب کاراکتر های “%” و "_"  باشید. در Yii Framework  با استفاده از توابع compare() و addSearchCondition() در criteria می توانید خیال خود را از این بابت آسوده کنید.

 

نکاتی در مورد کارایی برنامه :

توجه داشته باشید که یک پرس و جوی آماده شده (prepared statement) نسبت که پرس جو هایی که مستقیما در آنها عبارت SQL را می نویسیم، کند تر اجرا می شود. این مسئله به خودی خود معمولا گلوگاه کارایی سیستم شما نمی شود. اما در صورتی که قصد دارید یک پرس و جو را که از پارامتر های مقید شده استفاده می کند چندین مرتبه اجرا نمایید، در این صورت پرس و جو های آماده شده سریعتر عمل خواهند کرد.

همانطور که اشاره شد  استفاده از عبارات آماده شده (prepared statement) خطر injection را از بین می برد. اما با این وجود باز هم ممکن است شرایطی پیش آید که امکان استفاده از متغیر های مقید شده در عبارات SQL را نداشته باشیم. برای نمونه تکه کد SQL زیر را در نظر بگیرید:

 

SELECT *
  FROM {$mytable}
  WHERE {$myfield} LIKE '{$value}%' AND post_date < {$date}
  ORDER BY {$myfield}
  LIMIT {$mylimit}

یک راه سنتی برای برطرف نمودن این مشکل در PHP این است که لیستی (white-list)  از مقادیر قابل پذیرش برای هر متغیر را تعریف کنیم و ورودی کاربر را با آن چک نماییم. اما در Yii راه ساده تری وجود دارد چراکه Yii Framework  از schema پایگاه داده ما مطلع است. بهمین دلیل می توان بصورت زیر عمل نمود :

 

<?php
if (!Comment::model()->hasAttribute($myfield)) {
    die("Error");
}

یکی از راهکار های مناسب دیگر برای ایجاد پرس و جو های امن در Yii Framework استفاده از Query Builder است. این ابزار یکی از روش هایی است که می توان یک پرس و جو را با استفاده کد های خالص PHP ایجاد نمود، اما باید توجه داشته باشید که در هنگام استفاده از این ابزار امکان ترکیب سایر راهکار ها از جمله Criteria را از دست دست خواهید داد. برای آشنایی بیشتر بخش مربوط به Query Builder را در مستندات Yii Framework مطالعه کنید.

در اغلب اوقات برای ما مطلوب است که نتیجه یک پرس و جو بصورت یک مدل به ما بازگردانده شود. در چنین حالتی استفاده از متد های find() بهمراه criteria  می تواند امنیت را در کنار خواسته شما برایتان تامین کند. برای مثال به کد زیر توجه کنید :

 

<?php
// Yii applies some validity checks when the query is not raw SQL
$criteria = new CDbCriteria(
    array(
        'order' => $myfield,
        'limit' => $mylimit,
    )
);
$criteria->compare($myfield, $value, true); // LIKE % escaped($value) %
$criteria->compare('post_date', '<:date');
$criteria->params = array(':value' => $value, ':date' => $date);
$comments = Comment::model()->findAll($criteria)

قدرت و کارایی این روش وقتی بیش از پیش نمایان می شود که شما بخواهید از آن در CGridView استفاده کنید. همانطور که می دانید CGridView از یک CDataProvider بعنوان ورودی استفاده می کند که آن نیز بنوبه خود از CDbCriteria بهره می برد، استفاده از این روش باعث می شود که شما دیگر نگران ورودی های کاربر در هنگام جستجو بر روی داده های گرید نباشید.

کد زیر نسخه کامل شده مطالب بیان شده است :

 

<?php
// Yii applies some validity checks when the query is not raw SQL
$criteria = new CDbCriteria();
$criteria->order = $myfield;
$criteria->limit = $mylimit;
$criteria->addSearchCondition($myfield, $value, true); // true ==> LIKE '%...%'
$criteria->addCondition("post_date < :date");
$comments = Comment::model()->findAll($criteria, array(':value' => $value, ':date' => $date));

درپایان بعنوان جمع بندی پیشنهاداتی به شما عزیزان می شود :

هنگامی که می خواهید حاصل پرس و جوی شما مدل باشد بهتر است از این روش ها استفاده کنید : 

 

CActiveRecord::findByPk() or CActiveRecord::findAllByPk()
CActiveRecord::findByAttributes() or CActiveRecord::findAllByAttributes()
X::model()->find($criteria, array(':param1' => $value1)) or ->findAll(...)
X::model()->find($sql, array(':param1' => $value1)) or ->findAll(...)
X::model()->findBySql($sql, array(':param1' => $value1)) or ->findAll(...)

هنگامی که برایتان مهم نیست خروجی شما مدل باشد پیشنهاد می شود از عبارات آماده شده بشکل زیر استفاده نمایید :

 

<?php
$r = Yii::app()->db
    ->createCommand($sql)
    ->queryAll(array(':param1' => $value1));

البته باز هم فراموش نکنید که ورودی کاربر را پیش از هر کاری بررسی و اعتبار سنجی نمایید.

در پایان اگر خواهان برنامه ای امن هستید بعنوان یک اصل مهم در امنیت همواره بیاد داشته باشید که ورودی کاربر چیزی است که همواره باید از آن ترسید و هیچگاه نباید از آن غاقل شد.

منبع : امنیت در Yii Framework

hamedkh
5.8 k     1     18     72
نظرات
نظر داده نشده است
آموزش زبان برنامه نویسی C#
آموزش jquery

آموزش زبان جاوا Java
آموزش زبان انگلیسی
آموزش برنامه نویسی C
آموزش برنامه نویسی C++
آموزش جی کوئری jQuery
آموزش زبان سی شارپ C#
آموزش برنامه نویسی اندروید
آموزش برنامه نویسی اندروید
آموزش زبان اسمبلی Assembly
آموزش جاوا اسکریپت JavaScript
آموزش برنامه نویسی به زبان PHP