+۱۵ ترفند امنیتی برای بالا بردن امنیت برنامه PHP شما – قسمت دوم

daskhatmin فواد طهماسبی
17,797 بازدید
بالا بردن امنیت برنامه PHP

6- به رمزهای عبور نمک بزنید! و از رمزنگاری های قدرتمند استفاده کنید:
MD5 یکی از محبوب ترین الگوریتمهای رمزنگاری مورد استفاده توسط برنامه نویسان php است. تابع md5 داده رمزنگاری شده را به درستی برمیگرداند:

$hash = md5($password);

هرچند که، تابع md5 روش کاملا مطمئنی برای رمزنگاری نیست. اکثر کاربران تمایل دارند از رمزهای عبور 5،6 کاراکتری استفاده کنند، رمزهای عبور به این شکل که از پیچیدگی مناسبی برخوردار نیستند، توسط یک حمله brute-force  (جستجوی فراگیر) ساده نیز  آسیب پذیر خواهند بود. حتی ممکن است اصلا نیازی به حمله brute-force نباشد و فقط یک جستجوی ساده ی عبارت رمزنگاری شده (hash) در گوگل، رمزعبور را در نتایج وبسایتهای تخریبگر رمزعبور نشان دهد! و شاید بازهم ساده تر از این باشد. بنابراین، این کافی نیست که فقط به کابر بگویید رمزعبور پیچیده تر انتخاب کند.

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

$salt = 'SUPER_SALTY';
$hash = md5($password . $salt);

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

همراه با نمکپاشی، بهتر است از الگوریتمهای رمزنگاری طولانی تر (ولی کندتر) مانند sha1 , sha2  و … استفاده کنید. این نوع رمز نگاری چون زمان زیادی میبرد، برای مقابله با حملات جستجوی فراگیر مناسب است.

** پیوست :

یک تابع دیگر در php برای این منظور تابع crypt میباشد:

//string crypt ( string $str [, string $salt ] )
crypt('FoadTahmasebi', '$2a$07$usesomesillystringforsalt$')

خود من هم از این روش برای رمزنگاری استفاده میکنم:

$user->password = sha1(md5($password);

نمونه افزودن نمک که در وردپرس بکار رفته:

https://api.wordpress.org/secret-key/1.1/salt/

 

7- محافظت در برابر درخواستهای جعلی (CSRF):
CSRF مخفف عبارت Cross Site Request Forgery به معنی درخواستهای جعلی از سایت دیگر میباشد. این نوع آسیب پذیری نامشخص ترین نوع آسیب پذیری است چرا که اصلا به صورت یک مشکل به نظر نمیرسد. بیایید url ای را در نظر بگیریم که در پایگاه داده تغییر ایجاد میکند:

update_info.php?id=123
OR
delete_record.php?id=123

مشکل url بالا در این است که اگر هکر همچین url ای را با دسترسی کاربر بدون اطلاع وی بتواند اجرا کند چه اتفاقی می افتد؟

هکر لینک بالا را در لینک مسیر یک تصویر در سورس کد صفحه وب مربوط به خود (hackersite.com/interesting.html) قرار میدهد و از کاربر میخواهد وبسایت او را باز کند:

<img src="https://example.org/delete_record.php?id=123" />

حالا چون کاربر در برنامه شما لاگین نموده است (logged in) پس از باز نمودن صفحه مربوط به هکر url بالا هنگام لود تصویر فراخوانی میشود. و این یک درخواست قانونی است.

بنابراین در بحث فنی، مشکل اینجاست که سرور نمیتواند تشخیص دهد که کاربر با میل و علاقه خود این url را فراخوانی کرده یا خیر. از اینرو باید از مکانیسم خاصی استفاده کرد.

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

راه حل قوی تر و عمومی تر، فعال کردن سرویس جهت شناسایی هر درخواست با یک کلید است. فرم ها میتوانند دارای فیلد مخفی حاوی یک عبارت تصادفی باشند که در session کاربر ذخیره شود و برای تشخیص ارسال فرم بکار روند. کد صفحه مربوط به فرم:¹

<?php
session_start();
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
?>

<form action="delete_record.php" method="post" >
<input type="hidden" name="token" value="<?php echo $token ?>" />

<!-- Reminder of form -->
</form>

کد مربوط به اسکریپت پردازشگر:¹

if (isset($_SESSION['token']) 
&& isset($_POST['token']) 
&& $_POST['token'] == $_SESSION['token']){
// Token is valid, continue processing form data
}

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

 

8- بازسازی Session ID :
session id  را به صورت زیر میتوان بازسازی کرد:

session_regenerate_id(); //changes only session id
//or
session_regenerate_id(true);

session id حداقل باید در موارد زیر بازسازی شود:
– زمانی که کاربر وارد میشود (Log in)
– زمانی که کاربر خارج میشود (Log out)
– زمانی که کاربر وارد محیط مدیریتی میشود و یا سطح دسترسی کاربر تغییر میکند

if(valid_username() and valid_password())
{
    $_SESSION['logged'] = true;
    $_SESSION['username'] = $username;
}

شاید بخواهید session id را هر 15 دقیقه یا از هر 100 درخواست تغییر دهید:

session_start();

//increment and check
if ( ++$_SESSION['regenerated_count'] > 100 ) 
{
    //reset and regenerate
    $_SESSION['regenerated_count'] = 0;
    session_regenerate_id(true);
}

 

9- قفل کردن عامل کاربری در طول یک نشست (session):
این روش میتواند از ربودن (hijacking) نشست جلوگیری کند. این روش به این صورت است که تست میکنیم تا ببینیم آیا عامل کاربری (user agent) کاربر تغییر کرده است یا خیر. اگر تغییر کرده باشد، خارج شده (log out) و از کاربر درخواست ورود (log in) میکنیم:

//Function to check if user is logged in or not
function check_login_status()
{
    if($_SESSION['logged'] == true and $_SESSION['old_user_agent'] == $_SERVER['HTTP_USER_AGENT'])
    {
        return true;
    }

    return false;
}

if(!check_login_status())
{
    logout();
}

//Rest of the login protected page

و یا بهنر از آن:

session_start(); 

$chk = @md5( $_SERVER['HTTP_ACCEPT_CHARSET'] . $_SERVER['HTTP_ACCEPT_ENCODING'] . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . $_SERVER['HTTP_USER_AGENT']); 
if (empty($_SESSION)) 
{
	$_SESSION['key'] = $chk; 
}
else if ($_SESSION['key'] != $chk) 
{
	session_destroy();  
}

قفل IP روی session :
اگر امنیت بیشتر و قوی تری مورد نیاز است، میتوان آدرس IP کاربر را بر روی session  او قفل کرد:

session_start();

$chk = @md5( $_SERVER['HTTP_ACCEPT_CHARSET'] . $_SERVER['HTTP_ACCEPT_ENCODING'] . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . $_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']);

if (empty($_SESSION));
{
	$_SESSION['key'] = $chk;
}
else if ($_SESSION['key'] != $chk);
{
	session_destroy(); 
}

 

10- ذخیره نشستها (sessions) در پایگاه داده:
به صورت پیشفرض نشستها در فایل ذخیره میشود. تعداد زیادی برنامه وب بر روی میزبانهای اشتراکی (shared hosting) میزبانی میشوند که فایلهای نشستهای آنها در شاخه tmp/ ذخیره میشود. اگر این فایلها رمزنگاری نشده باشند، احتمالا برای کاربران دیگر قابل مشاهده خواهد بود:

userName|s:5:"ngood";accountNumber|s:9:"123456789";

برای جلوگیری از مشکل، راه حلهای متعددی وجود دارد. یکی از این روشها استفاده از رمزنگاری اطلاعات نشستها با suhosin است.

ذخیره نشستها در پایگاه داده. در صورت ذخیره نشستها در پایگاه داده، اطلاعات نشستها دیگر همانند فایلها قابل رویت نخواهد بود و فقط برنامه ای که آن را استفاده میکند به آن دسترسی خواهد داشت.

 


¹. Zend PHP 5 Certification STUDY GUIDE

 

قسمت اول | قسمت دوم | قسمت سوم

daskhatmin

فواد طهماسبی

فواد طهماسبی هستم، موسس وبسایت آموزشی دسخط. چندین سال تجربه کاری مرتبط با تکنولوژیهای توسعه وب دارم. علاقمند به ایده پردازی و توسعه کسب و کارهای اینترنتی هستم.

5 دیدگاه برای “+۱۵ ترفند امنیتی برای بالا بردن امنیت برنامه PHP شما – قسمت دوم”

  1. mehdi گفت:

    این کاملش

    عضویت درسایت
    <form method="post" action="”>

    <input type="text" name="name" placeholder="نام کاربری" value="”>
    *

    <input type="text" name="phone" placeholder="تلفن همراه" value="”>
    *

    <input type="email" name="email" placeholder="ایمیل" value="”>
    *

    <input type="password" name="pass" placeholder="رمزعبور" value="”>
    *

    query($user);
    $user = $result->fetch_assoc();

    if ($user) { // if user exists
    if ($user[‘name’] === $name) {
    echo” این نام کاربری ازقبل وجوددارد”;
    }

    if ($user[’email’] === $email) {
    echo”این آدرس ایمیل ازقبل وجود دارد”;
    }
    }

    else if(($passeror===””)&&($nameeror===””)&&($phoneeror===””)&&($emaileror===””)&&(!empty($name)&&(!empty($pass))&&(!empty($phone)&&(!empty($email))))){

    $sql=”insert into et2(id,name,phone,email,password)values(null,’$name’,’$phone’,’$email’,'”.md5($pass).”‘)”;
    $result=$con->query($sql);

    if($result){
    header(“Location: login.php”);

    }
    }
    ?>

  2. mehdi گفت:

    خیلی ممنون جواب میدید وکمک میکنید…عزیز این فرم من الان امنه؟…من نتونستن رمزو امنیتیش کنم..کردم تو قسمت لوگینم کارنکرد…این فرم منه.

    عضویت درسایت
    <form method="post" action="”>

    <input type="text" name="name" placeholder="نام کاربری" value="”>
    *

    <input type="text" name="phone" placeholder="تلفن همراه" value="”>
    *

    <input type="email" name="email" placeholder="ایمیل" value="”>
    *

    <input type="password" name="pass" placeholder="رمزعبور" value="”>
    *

    query($user);
    $user = $result->fetch_assoc();

    if ($user) { // if user exists
    if ($user[‘name’] === $name) {
    echo” این نام کاربری ازقبل وجوددارد”;
    }

    if ($user[’email’] === $email) {
    echo”این آدرس ایمیل ازقبل وجود دارد”;
    }
    }

    else if(($passeror===””)&&($nameeror===””)&&($phoneeror===””)&&($emaileror===””)&&(!empty($name)&&(!empty($pass))&&(!empty($phone)&&(!empty($email))))){

    $sql=”insert into et2(id,name,phone,email,password)values(null,’$name’,’$phone’,’$email’,'”.md5($pass).”‘)”;
    $result=$con->query($sql);

    if($result){
    header(“Location: login.php”);

    }
    }
    ?>

  3. chya گفت:

    ممنون
    مفید بود
    به کارتون ادامه بدین 🙂

  4. مهدی عدالت گفت:

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

    1. فؤاد طهماسبی گفت:

      ممنون، خواهش میکنم.

دیدگاه‌ها بسته شده‌اند.