آموزش ذخیره سازی ایمن Password در PHP و MySQL


گذرواژه را به هیچ وجه به صورت متن ساده ذخیره نکنید

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

بجای روش های ایمن و ثابت شده، مکانیزم اختصاصی خود را برای تامین امنیت گذرواژه پیاده سازی نکنید

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

گذرواژگان خود را رمزنگاری یا encrypt نکنید

برخی فکر می کنند که رمزنگاری پسورد روشی کارامد در تامین امنیت آن می باشد. اما هر کاربری که به کد شما دسترسی داشته باشد می تواند به راحتی نسخه ی رمزنگاری شده ی گذرواژه را به نسخه ی اصلی آن برگرداند. مکانیزم تامین امنیت با ایجاد ابهام (security by obsecurity) همیشه کارامد نیست.

از الگوریتم MD5 برای رمزنگاری گذرواژه استفاده نکنید

می توان گفت که ذخیره ی پسورد هش شده (password hash) راه مناسبی برای تامین امنیت آن می باشد. پسورد هایی که با تابع درهم سازی (cryptographic hashing function) رمزنگار هش می شوند قابل بازگردانی به نسخه ی اصلی خود نیستند. بنابراین هکر نمی تواند به راحتی نسخه ی اصلی پسورد را به دست بیاورد. به منظور اعتبارسنجی یک پسورد هش و درهم سازی شده، کافی است پسورد را بار دیگر هنگامی که کاربر وارد سایت می شود، هش نموده و سپس نسخه های هش شده را با یکدیگر مقایسه کنید.

$password = 'swordfish';  $hash = md5($password); // Value: 15b29ffdce66e10527a65bc6d71ad94d  

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

چرا نباید از MD5 استفاده کرد؟ چرا که به راحتی می توان لیستی از پسوردهای هش شده (Rainbow table = جدول معکوس توابع درهم سازی) گردآوری کرده و با مقایسه ی هش ها بایکدیگر، پسوردهای اصلی را پیدا کرد (این امر در مورد دیگر توابع درهم سازی مانند SHA-1 نیز صادق است).

MD5 همچنین به خاطر عدم ویژگی collision resistance (عدم مقاومت در برابر حمله ی تصادم)، در برابر brute forcing (حمله ی جستجوی فراگیر = حمله ی امنیتی که در آن تمام حالات ممکن تا رسیدن به جواب بررسی می‌گرد) آسیب پذیر می باشد. بدین معنی که پسوردهای مختلف می توانند یک هش داشته باشند که این امر پیدا کردن نسخه ی هش شده صحیح را هرچه آسان تر می کند.

از یک salt در کل سایت خود استفاده نکنید (site-wide salt)

salt رشته ای است که همراه با پسورد درهم سازی یا هش می شود به طوری که خروجی در جداول و فرهنگ نامه هایی که هش ها در آن گردآوری شده اند (rainbow table ها یا dictionary attack ها)، پیدا نشود. به عبارت دیگر salt رشته ای است که جهت امنیت بیشتر به اول یا آخر هش اضافه می شود.

$password = 'swordfish';  $salt = 'something random';  $hash = md5($salt . $password); // Value: db4968a3db5f6ed2f60073c747bb4fb5  

این روش در مقایسه با زمانی که تنها از MD5 استفاده می کنید، کاراتر تلقی می شود اما اگر کاربری به کد شما دسترسی داشته باشد، می تواند به راحتی رشته ی Salt را پیدا کرده و یک rainbow table جدید ایجاد کند.

کارهایی که شما باید برای رفع مشکل انجام دهید:

  • استفاده از یک تابع درهم سازی و رمزنگاری (cryptographic hash function) قدرتمند نظیر bcrypt (به تابع crypt() نیز نگاهی بیاندازید).
  • ویژه ی هر password از یک salt جداگانه استفاده نمایید.
  • از یک الگوریتم درهم سازی دقیق جهت افزایش مقاومت در برابر حملات brute force استفاده نمایید.
  • هر بار که کاربر وارد سایت می شود (لاگین می کند)، یک هش جدید تولید نمایید.
$username = 'Admin';  $password = 'gf45_gdf#4hg';  // A higher "cost" is more secure but consumes more processing power  $cost = 10;  // Create a random salt  $salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');  // Prefix information about the hash so PHP knows how to verify it later.  // "$2a$" Means we're using the Blowfish algorithm. The following two digits are the cost parameter.  $salt = sprintf("$2a$%02d$", $cost) . $salt;  // Value:  // $2a$10$eImiTXuWVxfM37uY4JANjQ==  // Hash the password with the salt  $hash = crypt($password, $salt);  // Value:  // $2a$10$eImiTXuWVxfM37uY4JANjOL.oTxqp7WylW7FCzx2Lc7VLmdJIddZq  

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

$username = 'Admin';  $password = 'gf45_gdf#4hg';  // For brevity, code to establish a database connection has been left out  $sth = $dbh->prepare('    SELECT      hash    FROM users    WHERE      username = :username    LIMIT 1    ');  $sth->bindParam(':username', $username);  $sth->execute();  $user = $sth->fetch(PDO::FETCH_OBJ);  // Hashing the password with its hash as the salt returns the same hash  if ( hash_equals($user->hash, crypt($password, $user->hash)) ) {    // Ok!  }  

تعدادی نکته ی دیگر برای جلوگیری از هک شدن حساب های کاربری:

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

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *