امنیت در PHP

Sharif_

مدیر بازنشسته
در این تاپیک امنیت در مورد PHP بحث خواهد شد.
لزا از دوستانی که ایده ، مطلب علمی یا مقاله ای دارند در این جا قرار بدند تا همگی استفاده کنند
اگر یک مشکل امنیتی هم وجود داشت بهتر هست که در این جا ذکر بشه تا راه حل مورد بحث قرار بگیره
 

Sharif_

مدیر بازنشسته
خب یکی از مهمترین مسائل امنیتی در مورد اشتباهات رایج و ساده در کد نویسی هستش
یک نمونه میارم و از دوستان تقاضا می کنم کمک کنند.

یکی از قسمت های مهم که مورد حمله قرار میگره پایگاه های SQL هست که به SQL Injection معروف هست
در یک مثال ساده ممکن هست در یک اسکریپ PHP از کاربر نام کاربری و رمزعبور درخواست بشه و سرور نسبت به صحیح بودن این امر DBMS مراجعه کنه
نمونه کد
PHP:
SELECT * FROM users WHERE name='$username' AND pass='$password';
اگر فردی به جای رمز عبور عبارت زیر رو وارد کنه :
PHP:
' OR '1'='1
حاصل Query تبدیل به کد زیر میشه و به پایگاه داده ارسال میشه
PHP:
SELECT * FROM users WHERE name='known_user' AND pass='' OR '1'='1';
با استفاده از خاصیت دستور منطقی OR و بدون تصدیق نام کاربری و رمز عبور صلاحیت اعمال میشه
برای حل این مشکل از تابع( ) addslashes استفاده میشه که بین ' ها برش ایجاد میکنه
پس برای بهینه سازی نام کاربری و رمز عبور از کد زیر استفاده میکنیم
PHP:
$username = addslashes($_POST["username"]);  
$password = addslashes($_POST["password"]);
اما این به تنظیمات PHP شما هم بستگی داره. با فعال کردن magic_quotes_gpc در PHP.ini میتونید addslashes رو برای تمامی مقدارهای گرفته شده از GET و POST یا کوکی ها به صورت خودکار اعمال کنید.
با استفاده از از تابع () stripslashes همچنین میتونید در صورت نیاز برش ها رو از مقادیر حذف کنید.
در این کد با بررسی مقدار magic_quotes_gpc عمل حذف برش درون مقادیر اعمال میشه

PHP:
if (get_magic_quotes_gpc()){  
  $_GET = array_map('stripslashes', $_GET);  
  $_POST = array_map('stripslashes', $_POST);  
  $_COOKIE = array_map('stripslashes', $_COOKIE);  
}

نمونه مثال برای توابع stripslashes و addslashes
PHP:
$str = "Is your name O'reilly?";

// Outputs: Is your name O\'reilly?
echo addslashes($str);
PHP:
$str = "Is your name O\'reilly?";

// Outputs: Is your name O'reilly?
echo stripslashes($str);
 
آخرین ویرایش:

baback

کاربر فعال
ممنون از sharif عزیز به خاطر این مطلبی که عنوان کردن چرا که رعایت نکات امنیتی از مهم ترین مسائل توی کدهای php هست ( وب به خودی خود یه حفره امنیتی بزرگ به حساب میاد، اگه قرار باشه ما توی کدهامون مسائل امنیتی رو رعایت نکنیم دیگه بدتر )

در رابطه با مطلب فوق هم توی توضیحات مربوط به قسمت magic_quotes_gpc یه اشتباهی وجود داره :

اما این به تنظیمات PHP شما هم بستگی داره. با غیر فعال کردن magic_quotes_gpc در PHP.ini میتونید addslashes رو برای تمامی مقدارهای گرفته شده از GET و POST یا کوکی ها به صورت خودکار اعمال کنید.

که صحیحش این هست که با فعال کردن تابع فوق ، addslash برای تمامی مقادیر GET و POST یا کوکی ها به صورت خودکار اعمال میشه .

مثالی هم که در ادامه زده شده صحیح هست و گویای همین مسئله :

PHP:
if (get_magic_quotes_gpc()){  
  $_GET = array_map('stripslashes', $_GET);  
  $_POST = array_map('stripslashes', $_POST);  
  $_COOKIE = array_map('stripslashes', $_COOKIE);  
}

در واقع کد فوق این رو میگه که اگر مقدار تابع مورد نظر true باشه ( خروجی تابع فوق زمانی که مقدار get_magic_quotes_gpc در فایل پیکربندی on باشه ، true خواهد بود ) پس بهتر هست که slash های مقدار گرفته شده رو با تابع stripslashes حذف کنیم . چون در ادامه قرار هست با تابع addslashes به مقدار گرفته شده ، slash اضافه کنیم چرا که اگه این کار انجام نشه در واقع ما به مقدارمون 2 بار backslash اضافه کردیم . البته این ایرادی نیست ( البته در این مواقع ) چون ما میخوایم که در هر صورت query خودمون رو نا مفهوم کنیم برای mysql اما مواقعی که مثلا خودمون به عنوان admin از پایگاه خروجی بگیریم و یا صفحه ای داشته باشیم که بخواد خروجی ای از بانک رو به کاربر نشون بده که دارای کاراکتر خاص باشه ، اونوقت هست که به مشکل برمیخوریم. مثال برای مقدار O'reilly

PHP:
<?php
echo get_magic_quotes_gpc();         // 1
echo $_POST['lastname'];             // O\'reilly
echo addslashes($_POST['lastname']); // O\\\'reilly
 
if (!get_magic_quotes_gpc()) {
    $lastname = addslashes($_POST['lastname']);
} else {
    $lastname = $_POST['lastname'];
}
 
echo $lastname; // O\'reilly
$sql = "INSERT INTO lastnames (lastname) VALUES ('$lastname')";
?>


اما در مورد qury injrction ....
بالای 90 درصد نسخه های مختلف php که از get_magic_quotes_gpc برای رخ ندادن این وضعیت پشتیبانی میکنن همیشه به طور پیش فرض مقدار اون رو on قرار میدن. یعنی خود موتور php برای کاراکترهای خاص که برای پایگاه داده مفهوم خاصی داره قبلش یه backslash اضافه میکنه تا query برای sql نامفهوم بشه و اون رو اجرا نکنه اما بهتر که خودمون در کدها چنین قابلیتی رو اضافه کنیم ( به دلایلی ) .

اگر بخوایم از تابع addslshes استفاده کنیم همون طور که دوستمون sharif توضیح داد باید قبلش چک کنیم که get_magic_quotes_gpc فعال هست یا نه که اگر نبود addslash انجام بدیم. اما اگر میخوایم که تحت هر شرایطی از تابع addslashes استفاده کنیم پس بهتره بعد از چک کردن اگر مقدار مقدار on بود ابتدا stripslashes رو صدا بزنیم و بعد addslashesh که این هم توی پست قبلی کامل توضیح داده شد .

اما چیزی که هست اینکه این تابع Addslashes در کل برای این هست که اگر یک زمانی شما خواستین مقادیر حاصل از فرمی رو یا خروجی حاصل از پایگاهتون رو که کاراکترهای خاص مثل ' یا '' دارن توی صفحه چاپ کنید به مشکل بر نخورید . برای همین از این تابع استفاده میکنیم که php خودش قبل از کاراکترهای خاص یه backslash اضافه کنه تا مشکلی برای پردازش اونها پیش نیاد ( برای php نه mysql )

تابعی که در php برای این کار ایجاد شده و خود manuel php هم سفارش میکنه ، تابع mysql_real_escape_string هست که این تابع هم همون کار ف ی ل ت ر کردن کاراکترهای خاص رو انجام میده اما قبلش یادتون باشه که چک کنید اگر مقدار get_magic_quotes_gpc ، on بود دیگه دوباره کاری نشه .

من از خود manuel سایت php استفاده میکنم که دقیق و بسیط هست :

mysql_real_escape_string() calls MySQL's library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.
This function must always (with few exceptions) be used to make data safe before sending a query to MySQL.​

اینهم مثالش که با پارامترهای ورودی اون آشا بشین :

PHP:
<?php
 
if (isset($_POST['product_name']) && isset($_POST['product_description']) && isset($_POST['user_id'])) {
    // Connect
 
    $link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password');
 
    if(!is_resource($link)) {
 
        echo "Failed to connect to the server\n";
        // ... log the error properly
 
    } else {
 
        // Reverse magic_quotes_gpc effects on those vars if ON.
 
        if(get_magic_quotes_gpc()) {
            $product_name        = stripslashes($_POST['product_name']);
            $product_description = stripslashes($_POST['product_description']);
        } else {
            $product_name        = $_POST['product_name'];
            $product_description = $_POST['product_description'];
        }
 
        // Make a safe query
        $query = sprintf("INSERT INTO products (`name`, `description`, `user_id`) VALUES ('%s', '%s', '%d')",
                    mysql_real_escape_string($product_name, $link),
                    mysql_real_escape_string($product_description, $link),
                    $_POST['user_id']);
 
        mysql_query($query, $link);
 
        if (mysql_affected_rows($link) > 0) {
            echo "Product inserted\n";
        }
    }
} else {
    echo "Fill the form properly\n";
}
?>
 

baback

کاربر فعال
مورد دیگه ای که باید توی کدهای php بهش توجه بشه این هست که error ها و پیغام های خطای کد شما مخصوصن زمانی که با DB ارتباط برقرار میکنه به کاربر نمایش داده نشه .چون به این ترتیب اطلاعاتی درباره web server و همین طور جداول db به همراه نام فیلدها و نوع دسترسی به اونها و .... نمایش داده میشه که اگه کاربر قصد شیطونی داشته باشه ! اونوقت میتونه مشکلی رو فراهم کنه . برای اینکه پیغام های خطا نمایش داده نشه میتونید از طریق فایل htaccess. نمایش خطارو از کار بندازید که اکثر هاست ها این کار رو بطور پیش فرض انجام میدن اما خوب در مواردی آگاهی از پیغام های خطا برای admin میتونه راه گشا باشه برای همین میتونید پیغام خطا در فایل htaccess فعال کنید و به جای اون یا قبل ازشروع توابع علامت @ قرار بدین و یا اینکه ورود تابع ()error_reporting رو با مقدار 0 مقداردهی کنید .

تابعد ...
 

mystack

عضو جدید
مورد دیگه ای که باید توی کدهای php بهش توجه بشه این هست که error ها و پیغام های خطای کد شما مخصوصن زمانی که با DB ارتباط برقرار میکنه به کاربر نمایش داده نشه .چون به این ترتیب اطلاعاتی درباره web server و همین طور جداول db به همراه نام فیلدها و نوع دسترسی به اونها و .... نمایش داده میشه که اگه کاربر قصد شیطونی داشته باشه ! اونوقت میتونه مشکلی رو فراهم کنه . برای اینکه پیغام های خطا نمایش داده نشه میتونید از طریق فایل htaccess. نمایش خطارو از کار بندازید که اکثر هاست ها این کار رو بطور پیش فرض انجام میدن اما خوب در مواردی آگاهی از پیغام های خطا برای admin میتونه راه گشا باشه برای همین میتونید پیغام خطا در فایل htaccess فعال کنید و به جای اون یا قبل ازشروع توابع علامت @ قرار بدین و یا اینکه ورود تابع ()error_reporting رو با مقدار 0 مقداردهی کنید .

تابعد ...

بعضی از هاست ها بکل امکان set کردن رو روی php.ini رو غیرفعال کردن. باید از روش های پیشرفته تر هندلینگ خطا استفاده کرد ......
مگه اینکه سرور ماله خودتون باشه که اون وقت بله :cool:
 

Sharif_

مدیر بازنشسته
یکی از اصل های مهم امنیت در برنامه نویسی این هست که میگه به داده ای که از خارج دریافت می کنی اعتماد نکن.
نکته دوم خام کردن داده قبل از ارسال هست که در اصطلاح به escape معروف هست
با ترکیب این دو قانون می توان یک اصل امنیت ایجاد کرد که داده ورودی رو فیلتـر و داده خروجی رو escape کنه که به عنوان FIEO شناخته شده است
دلیل اصلی در SQL injection عدم موفقیت در escape کردن داده هاست. در واقع SQL injection زمانی اتفاق می افته که داده خام به عنوان یک پرس و جو تلقی میشه
کد زیر یک ساختمان کلی از ارسال کوئری هست
PHP:
<?php
$query = "SELECT *
          FROM   users
          WHERE  name = '{$_GET['name']}'";
?>
در این حالت مقدار $_GET['name'] نه فیـلتر شده است و نه عمل escape روی داده ارسال شده انجام شده است

همان طور که گفتیم هیچ اعتمادی بر روی داده ارسالی از خارج نداریم از این رو قبل از استفاده از متغییر نا مطمئن داده ها رو نسبت به کلمات حساسی چون ‘ و حروف دیگر بررسی می کنیم سپس برای احتیاط داده فیلـتر شده رو با استفاده از دستور mysql_real_escape_string() نسبت نبود قالب دستوری متغییر اطمینان حاصل می کنیم

دستور mysql_real_escape_string به دو صورت دو پارامتری و یک پارامتری ارسال میشه که در حالات نخست پارامتز اول به عنوان رشته و پارامتر دوم یک connection برای SQL استفاده میشه. در حالت تک پارامتر فقط پارامتر رشته ارسال میشه و connection اخرین ارتباط رو در نظر می گیره و اگر ارتباطی باز نبود با استفاده تابع بدون پارامتر mysql_connect() شروع به ایجاد ارتباط میکنه و اگر ارتباطی ایجاد نشد خطای warning ارسال می شود.
مقدار های بازگشتی در این دستور یا رشته escape شده هست یا false در هنگام خطا
وجود چنین کاراکترهایی باعث بروز خطا میشه چون قالب دستوری دارند
HTML:
\x00
\n
\r
\
'
"
\x1a
استفاده از این دو روش در ارسال داده، امنیت در عمق نامیده میشه


PHP:
<?php
     
    // تعریف تابع به ترتیب برای فیـلتر و اسکیپ داده
    $clean = array();
    $sql = array();
     
    // فلـتر کردن داده (مثلا فقط داده های حروفی)
    if (ctype_alpha($_GET['name'])) {
        $clean['name'] = $_GET['name'];
    } else {
        // دستوراتی برای داده نامعتبر
    }
     
    // اسکیپ داده فیلـتر شده
    $sql['name'] = mysql_real_escape_string($clean['name']);
     
    // ارسال پرس و جو
    $query = "SELECT *
              FROM   users
              WHERE  name = '{$sql['name']}'";
     
?>

مثالی دیگر از کاربرد تابع escape

PHP:
  <?php
// اتصال
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
    OR die(mysql_error());

// پرس و جو
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
            mysql_real_escape_string($user),
            mysql_real_escape_string($password));
?>

 

Sharif_

مدیر بازنشسته
همواره باید در دستور Select نکات امنیتی را رعایت کنید. چرا چون با تزریق دستورات به query مورد نظر می توان اطلاعات خاصی را استخراج کرد

PHP:
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'test@example.com' AND passwd = 'hello123';
در مثال بالا ما با ترزیق عبارت زیر شروع به حدس زدن پسورد کاربر کردیم
و با وصل کردن فیلد پسورد به یک دیکشنری امکان پیدا کردن پسورد امکان پذیر میشه
HTML:
test@example.com' AND passwd = 'hello123
 

AkramBagha

عضو جدید
سلام.میشه واسه وارد کردن پسورد هم از توابع کدگذاری استفاده کرد تا اگه کسی تونست به Data base دسترسی پیدا کنه نتونه به پسورد شخصی دسترسی داشته باشه

طریقه وارد کردن کد:
PHP:
function scrutiny($input)
{
	$input=trim($input);
	return ((get_magic_quotes_gpc()) ? $input : addslashes($input));
}

$username=scrutiny($_POST['username']);
$password=md5(scrutiny($_POST['password']));

$query="INSERT INTO users (`usernam`, `password`) 
VALUES ('".$username."','".$password."');";

طریقه باز یابی:

PHP:
if(md5($_POST['pass']) == $my_pass)
{
echo 'you are available';  
}
 

me.fatima

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

تو اين مقاله نوشته شده که استفاده از تابع هاي امنيتي فقط وقتي استفاده ميشه که بخواهيم داده اي رو از کاربر بگيريم.

حالا من داشتم يه کدي مربوط به سبد خريد نگاه ميکردم :

که وقتي کاربر روي Add کليک ميکنه id کالا رو به بطور مثال به صفحه cart.php ارسال ميکنه.
يعني اين کدشه :

PHP:
function products()
        {
        $get=mysql_query('select id,name,description,price from products where quantity>0 ' );
       
    while($get_row= mysql_fetch_assoc($get))   
print ($get_row['name']."<br>".$get_row['description']."<br>"."price  : ".$get_row['price'].
"<br>".'<a href="cart.php?add='.$get_row['id'].'">Add</a>'."<p>");     
 }


بعد در صفحه cart کد به اين صورت نوشته شده : ( یعنی جایی که میخواد id ارسال شود ) :

PHP:
if (isset ($_GET['add'] ))
        { 
            $quantity= mysql_query('select id,quantity from products where id='.mysql_real_escape_string((int))$_GET['add']) ;
            while($quantity_row= mysql_fetch_assoc($quantity))
            {
                     ادامه دستورات ....
            }
        }

يعني مياد از تابع mysql_real_escape_string براي id استفاده ميکنه . ولي چرا؟

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

nabshifarsi

عضو جدید
سلام و ممنون از مطالب و آموزش های خوبتون
یک مورد مهم که فکر میکنم همه به نوعی به اون اشاره کردن این هست که صفحه ورود پنهان بشه
صفحه ورود معمول برای ادمین همیشه با /admin از نام دامنه جدا میشه
و بات ها و اکثر عملیات مخرب از این آدرس برای اتک استفاده میکنن
اگر نام این صفحه تغییر کنه از بسیاری حملات پیش فرض جلوگیری میشه

توی این وبسایت https://markazahan.com/kavir-kashan-rebar-prices/ دقیقا از همین شیوه استفاده شده
که میتونید به راحتی اون را بررسی کنید
برای حفظ امنیت از ذکر عنوان صفحه ورود اجتناب میکنم
پایدار باشید
 

iranlaw

عضو جدید
سلام دوستان وقت تون بخیر
من یه سوال داشتم برنامه php رو باید از کجا شروع کنم
ایا اموزش خاصی مد نظر تون هست
 

jack_x

عضو جدید
خب یکی از مهمترین مسائل امنیتی در مورد اشتباهات رایج و ساده در کد نویسی هستش
یک نمونه میارم و از دوستان تقاضا می کنم کمک کنند.

یکی از قسمت های مهم که مورد حمله قرار میگره پایگاه های SQL هست که به SQL Injection معروف هست
در یک مثال ساده ممکن هست در یک اسکریپ PHP از کاربر نام کاربری و رمزعبور درخواست بشه و سرور نسبت به صحیح بودن این امر DBMS مراجعه کنه
نمونه کد
PHP:
SELECT * FROM users WHERE name='$username' AND pass='$password';
اگر فردی به جای رمز عبور عبارت زیر رو وارد کنه :
PHP:
' OR '1'='1
حاصل Query تبدیل به کد زیر میشه و به پایگاه داده ارسال میشه
PHP:
SELECT * FROM users WHERE name='known_user' AND pass='' OR '1'='1';
با استفاده از خاصیت دستور منطقی OR و بدون تصدیق نام کاربری و رمز عبور صلاحیت اعمال میشه
برای حل این مشکل از تابع( ) addslashes استفاده میشه که بین ' ها برش ایجاد میکنه
پس برای بهینه سازی نام کاربری و رمز عبور از کد زیر استفاده میکنیم
PHP:
$username = addslashes($_POST["username"]);  
$password = addslashes($_POST["password"]);
اما این به تنظیمات PHP شما هم بستگی داره. با فعال کردن magic_quotes_gpc در PHP.ini میتونید addslashes رو برای تمامی مقدارهای گرفته شده از GET و POST یا کوکی ها به صورت خودکار اعمال کنید.
با استفاده از از تابع () stripslashes همچنین میتونید در صورت نیاز برش ها رو از مقادیر حذف کنید.
در این کد با بررسی مقدار magic_quotes_gpc عمل حذف برش درون مقادیر اعمال میشه

PHP:
if (get_magic_quotes_gpc()){  
  $_GET = array_map('stripslashes', $_GET);  
  $_POST = array_map('stripslashes', $_POST);  
  $_COOKIE = array_map('stripslashes', $_COOKIE);  
}

نمونه مثال برای توابع stripslashes و addslashes
PHP:
$str = "Is your name O'reilly?";

// Outputs: Is your name O\'reilly?
echo addslashes($str);
PHP:
$str = "Is your name O\'reilly?";

// Outputs: Is your name O'reilly?
echo stripslashes($str);

همچنین میتونید از تابع htmlentities برای جلوگیری از حملات xss استفاده کنید ;)
 

Similar threads

بالا