PDA

View Full Version : bcrypt - الگوریتم هش پسورد حرفه ای



eshpilen
چهارشنبه 07 خرداد 1393, 09:14 صبح
سه مورد از خفن ترین الگوریتم های هش پسورد اینا هستن (به ترتیب رتبه):

1- scrypt

2- bcrypt

3- PBKDF2

شاید بپرسید پس چرا از scrypt کم استفاده میشه و بیشتر افراد بجاش اسم از bcrypt میارن.
باید بگم که این بخاطر جدید بودن scrypt است و اینکه به حد اون دو الگوریتم دیگر جا نیافتاده و شناخته نشده و پیاده سازیش در همه جا براحتی در دسترس نیست. البته متخصصان امنیت هم درمورد الگوریتم هایی که زیاد سابقه نداشتن و روشون توسط افراد و متخصصان زیادی در طول زمان قابل توجهی بررسی و تست و تحلیل صورت نگرفته، یک مقدار تردید دارن طبیعتا و بخاطر همین زیاد روی استفاده از اونا پافشاری نمیکنن. اینکه یک الگوریتم مدت زیادی جا افتاده باشه و توسط عدهء زیادی استفاده شده باشه و از زیر تیغ متخصصان و هکرهای زیادی گذشته باشه و تاحالا ضعف جدی درش دیده نشده باشه، باعث میشه ضریب اطمینان از بی نقص بودن اون بالا بره و نسبت بهش اعتماد عمومی بیشتری بوجود بیاد. بخاطر همین، درحال حاضر bcrypt خیلی بیشتر از scrypt شناخته شده است و استفاده میشه؛ البته یک دلیلش هم میتونه این باشه که امنیت bcrypt برای بیشتر برنامه ها درحال حاضر، کم و بیش کافی فرض میشه (بخصوص از این نظر که بیشتر برنامه ها در اون حدی بی نقص و قوی نیستن که امنیت حداکثری هش پسورد در این حد اعلی بخواد نگرانی اصلی در اونا باشه - بهرحال برای برنامه های بسیار حرفه ای و بی نقص، scrypt در آینده بر bcrypt برتری خواهد داشت).

در پستهای بعدی مطالب بیشتری خواهم گذاشت، و همچنین کد PHP برای الگوریتم bcrypt رو درج میکنم.

eshpilen
چهارشنبه 07 خرداد 1393, 11:08 صبح
bcrypt بهترین نوع شهرتی را که یک الگوریتم رمزنگاری میتواند بدان دست یابد داراست: آن کاملا برای مدت قابل توجهی حضور داشته است، بصورت گسترده ای استفاده شده است، جلب توجه کرده است، و هنوز تا این زمان شکسته نشده است.

چرا bcrypt بهتر از PBKDF2 است؟

bcrypt یک الگوریتم هش است که میخواهد کند باشد. به بیان دقیق تر، ما میخواهیم تابع هش پسورد تاجاییکه ممکن است برای حمله کننده کند باشد در عین حالی که برای سیستمهای غیر حمله کننده (م: که از این پس سیستمهای عادی نامیده میشوند) به میزان غیرقابل تحملی کند نباشد. از آنجاییکه سیستمهای عادی تمایل دارند تا از سخت افزارهای عمومی موجود در بازار استفاده کنند (یک PC) که همچنین در دسترس حمله کننده هستند، بهترین چیزی که ما میتوانیم امیدوار باشیم این است که هش کردن پسورد را N برابر برای هردوی خودمان و حمله کننده کند کنیم. ما میتوانیم N را چنان تنظیم کنیم تا از منابع ما (از همه مهمتر، شکیبایی کاربر، که واقعا محدود است) تجاوز نکند.

ما میخواهیم از اینکه یک کرکر ممکن باشد از یک سخت افزار غیر PC که به او اجازه خواهد داد تا کمتر از ما از کار اضافه ای که بوسیلهء bcrypt یا PBKDF2 درنظر گرفته شده است رنج ببرد اجتناب کنیم. به ویژه، یک هکر زبردست که میخواهد از یک GPU یا FPGA استفاده کند. برای نمونه، SHA256 میتواند بصورت بسیار کارایی بر روی یک GPU پیاده سازی شود، چراکه آن فقط از منطق 32 بیتی و عملیات عددی ای که GPU در آن بسیار خوب عمل میکند استفاده میکند. بنابراین، یک هکر با 500 دلار GPU قادر خواهد بود که تعداد خیلی بیشتری پسورد را در هر ساعت نسبت به آنچه که او میتوانست با یک PC به ارزش 500 دلار انجام دهد، تست کند (ضریب آن به نوع GPU بستگی دارد اما یک ضریب 10 یا 20 برابر معمول است).

bcrypt بصورت سنگینی بر دسترسی به یک جدول که بطور پیوسته در طول اجرای الگوریتم دستکاری میشود اتکا دارد. این بر روی یک PC خیلی سریع است، اما روی یک GPU که حافظه مشترک است و تمام هسته ها برای کنترل گذرگاه درونی حافظه رقابت میکنند خیلی کمتر سریع است. ترقی ای که یک حمله کننده بوسیلهء استفاده از GPU میتواند بدست بیاورد، در مقایسه با آنچه که حمله کننده میتواند با PBKDF2 یا طراحی های مشابه بدست بیاورد، کاملا کاهش یافته است.

طراحان bcrypt از این مسئله کاملا آگاه بودند، که دلیل آن است که چرا آنها bcrypt را با الگوریتم رمز Blowfish طراحی کردند و نه یک تابع از خانوادۀ SHA.

چرا bcrypt بصورت بهینه ای امن نیست؟

طراحان bcrypt در سال 1999 مشغول کار بودند. در آن زمان، تهدید، سخت افزارهای ASIC (م: سخت افزارهای سفارشی مخصوص یک کاربرد خاص) با تعداد دروازه های (gate) منطقی خیلی کم بود. زمان تغییر کرده است؛ اکنون، حمله کنندهء خبره از یک FPGA بزرگ استفاده خواهد کرد، و مدلهای جدید (بطور مثال Virtex از شرکت Xilinx) بلاکهای RAM گنجانده شده دارند، که به آنها اجازه میدهد تا Blowfish و bcrypt را بصورت خیلی کارایی پیاده سازی کنند. bcrypt تنها به 4 کیلوبایت از RAM سریع نیاز دارد. درحالیکه bcrypt در دشوار کردن زندگی برای یک هکر تقویت شده بوسیلهء GPU کارش را به خوبی انجام میدهد، درمقابل یک هکر مجهز به FPGA کار خیلی کمی میکند.

این Colin Percival را برانگیخت تا scrypt را در 2009 اختراع کند؛ این یک تابع شبیه bcrypt است که به RAM بسیار بیشتری نیاز دارد. این هنوز یک طراحی جدید است (فقط 2 سال) و به هیچ وجه بقدر bcrypt در سطح گسترده مورد استفاده قرار نگرفته؛ من آنرا برای توصیه کردن بعنوان یک مشی عمومی بیش از حد جدید میپندارم. اما مسیر آن باید دنبال گردد.

NIST چه چیزی را توصیه میکند؟

آنها اساسا PBKDF2 را توصیه میکنند. این به معنای آن نیست که آنها bcrypt را غیرامن یافته اند؛ آنها دربارهء bcrypt هیچ چیز نمیگویند. آن تنها بدان معنی است که NIST الگوریتم PBKDF2 را «بقدر کافی امن» دانسته (و قطعا آن از یک هش ساده بسیار بهتر است). همچنین، NIST یک سازمان اداری/اجرایی است، بنابراین آنها به دوست داشتن هرچیزی که بر اساس الگوریتم های قبلا تصویب شده مثل SHA256 بنا میشود محدود هستند. از طرف دیگر، bcrypt از Blowfish می آید که هیچوقت مورد هیچ نوعی از تایید یا رد NIST قرار نگرفت.

درحالیکه من bcrypt را توصیه میکنم، من هنوز NIST را همراهی میکنم که اگر شما PBKDF2 را به درستی پیاده سازی و استفاده کنید (با یک تعداد دور/تکرار بالا)، آنگاه کاملا محتمل است که ذخیرهء پسورد دیگر بدترین مسئلهء امنیتی شما نباشد.

===========================

منبع: http://security.stackexchange.com/a/6415

eshpilen
پنج شنبه 08 خرداد 1393, 08:32 صبح
اینم کدهای استفاده از bcrypt که بهبود یافته (ضمیمه شد).

بهبودهاش چیه حالا؟

- از یک تابع رندوم کاملتر و در نتیجه امن تر استفاده کردم (این تابع رو قبلا توی وبلاگ خودم (http://www.hamidreza-mz.tk/?p=649) گذاشته بودم).
- یک pepper هم به فرایند هش اضافه شده.
- محدودیت طول ورودی bcrypt رو برطرف کردم.

نکات و توضیحات:

- تابع رندومی که گذاشتم خارج از کلاس bcrypt است که قبل از استفاده از bcrypt باید اینکلود کنید (نمونه کدش در فایل bcrypt-test.php هست). ضمنا این تابع خیلی محکم کاری کرده و علاوه بر منابع رندوم امنیتی سیستم که بطور معمول کافی فرض میشن، از خود درخواست هم آنتروپی افزوده گرفته (به متغییر ‎$entropy در فایل func_crypt_random.php توجه کنید)؛ این شاید وسواسی بنظر بیاد، ولی بنظر من خوبه، و چه ضرری داره؟ فقط طبیعتا این تابع از بقیهء توابع پردازش بیشتری مصرف میکنه و کندتره، که اینم اکثرا نباید مشکل جدی پیش بیاره (چون در اون حد کند نیست و تولید رشته های رندوم هم اغلب تنها بخش کوچکی از کل پردازش برنامه هاست که در زمانهای محدودی صورت میگیره)، ولی اگر مشکل پرفورمنس دیده شد یا متوجه شدید که به این خاطر مشکل عملی جدی در برابر حمله های DOS بوجود میاد اونوقت میشه دستکاریش کرد و پرفورمنسش رو بهبود داد.

- pepper ما یک رشتهء رندوم hardcode شده در سورس برنامه است که در فرایند هش دخالت داده میشه. این باعث میشه که اگر فقط هش ها (محتویات دیتابیس) به طریقی بدست هکر افتاده ولی دسترسی به فایل برنامه نداشته، نتونه به هیچ وجه هش ها رو کرک کنه. باید هر سایتی یک مقدار رندوم دلخواه رو برای pepper تعیین کنه (موقع نصب برنامه ها و قبل از اینکه هیچگونه اکانتی در دیتابیس ثبت شده باشه) که توصیه میکنم یک رشتهء رندوم 22 کاراکتری متشکل از حروف بزرگ و کوچک و اعداد باشه تا امکان حدس زدن یا Brute-force اون منتفی باشه.
راستی از این pepper در تابع رندوم هم بعنوان منبع آنتروپی افزوده جهت افزایش امنیت استفاده میشه و به همین علت این متغییر در فایل func_crypt_random.php قرار دارد.
نکتهء دیگری که باید توجه کنید اینه که وقتی از دیتابیس پشتیبان میگیرید، مقدار هش های موجود در دیتابیس به اون مقدار pepper خاصی که هش ها در ترکیب با اون تولید شدن هم بستگی داره و اگر مقدار pepper هم در فرایند ریستور کردن داده های دیتابیس، به مقدار درست خودش برگردانده نشه، هش های شما کاملا غیرقابل استفاده خواهند بود و کاربران نمیتونن به سایت لاگین کنن و نیاز هست تا پسورد همشون ریست بشه! درواقع بقول معروف، pepper یک کلید هست و به این وابستگی و دنگ و فنگ هایی هم که گفتم «مدیریت کلید» گفته میشه؛ داشتن هر کلید، مستلزم نیازهای مدیریت اون هم هست. بنابراین توصیه میشه از pepper خودتون هم در یک جای امنی یک نسخهء پشتیبان داشته باشید (البته در دیتابیس و کنار هش ها نباشه، چون اینطوری دیگه خاصیت چندانی نداره و اسمش pepper نمیشه).

- بر اساس تست و اطلاعاتی که از قبل داشتم و دوباره در اینترنت هم سرچ و بررسی کردم، ورودی ای که به bcrypt میدید محدودیت طول داره که البته در نسخه ها و محیطهای مختلف ممکنه کمی تفاوت کنه اما حداکثر 72 کاراکتر/بایت است. این محدودیت درمورد اکثر پسوردها مشکلی نیست اما بعضی وقتا هم ممکنه مطلوب نباشه و بر اساس اصولش طول پسورد رو نباید تا این حد محدود کرد؛ بخصوص در برنامهء ما چون قبل از هش کردن pepper رو هم به ابتدای پسورد کاربر اضافه میکنیم، این محدودیت بیشتر میشه و به 50 کاراکتر میرسه (22 کاراکتر رو خود ‎‎ pepper اشغال میکنه). برای رفع این محدودیت، بنده رشتهء ورودی (pepper+password) رو قبل از اینکه به bcrypt بدیم، از تابع sha256 عبور دادم. تاجاییکه در اینترنت هم سرچ و بررسی کردم، این روش توسط افراد دیگری هم به همین خاطر پیشنهاد شده و بدون اشکال دانسته شده. البته تنها جنبهء منفی این کار اینه که سازگاری هش های شما با هش ها و برنامه های دیگری که از این تکنیک استفاده نمیکنن از بین میره (ولی فکر نمیکنم در اکثر موارد اهمیت خاصی داشته باشه).
ضمنا اینکه میگیم طول ورودی bcrypt محدودیت داره به این معنا نیست که مثلا نمیشه یک پسورد 100 کاراکتری رو به bcrypt داد تا برامون هش کنه، بلکه بدین معناست که bcrypt تنها از 72 کاراکتر اول اون پسورد استفاده و اونا رو هش میکنه؛ در نتیجه دو پسورد مثلا با طول 100 اگر تنها 72 کاراکتر اول اونها یکسان باشه، در سیستم هش و احراز هویت ما غیرقابل تفکیک خواهند بود، با وجود اینکه کاراکترهای دیگر اونا متفاوت باشن.
این محدودیت طول پسورد در این حد، بر اساس تحقیقات جدیدی که در نت کردم (قبلا هم مستقلا در این مورد مطلب داده بودم (http://www.hamidreza-mz.tk/?p=656)) از نظر متخصصان و افراد مطلع درست نیست، هرچند احتمالا طراحان bcrypt اینطور فکر نکردن. ضمنا باید توجه داشت که مثلا هر کاراکتر فارسی از 2 بایت تشکیل شده که این باعث میشه اگر کسی پسوردی متشکل از حروف فارسی (یا این همه زبانهای دیگر با حروف چندبایتی) انتخاب کنه، اون محدودیت 72 یا 50 کاراکتر بازهم بیشتر بشه.
بهرحال ما با هش کردن ورودی قبل از دادن اون به bcrypt، این محدودیت رو کاملا از بین بردیم و دیگه نیازی نیست نگران محدودیت طول ورودی هش باشیم. چون طول خروجی SHA256 همیشه یک مقدار ثابت و کمتر از محدودیت طول ورودی bcrypt است.