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

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

سالت ثابت ما اسمش مثلا pepper باشه.

اطلاعات دیتابیس ما به این شکله:

user1 salt1 hash1
user2 salt2 hash2
user3 salt3 hash3
...

هش های ما با این فرمول تولید شدن:

$hash_n=md5($pepper.$salt_n.$password_n)

منظور از _n یعنی همون شماره ها که در بالا میبینید. یعنی هش کاربر ان ام، سالت کاربر ان ام، پسورد کاربر ان ام.
نکته: استفاده از md5 فقط بخاطر سادگی مثال و آشنایی همگانی هست و ما در شرایط واقعی از این تابع ضعیف استفاده نخواهیم کرد.

حالا هکر میاد و دیتابیس رو میخونه، ولی نتونسته سالت ثابت رو بدست بیاره.
در این صورت هکر قادر نیست هیچ پسوردی رو روی هش های دیتابیس تست کنه.
چون مثلا میخواد هش پسوردی مثل love رو تست کنه، باید اول pepper رو داشته باشه تا بتونه هش صحیحی رو که سیستم ما با چنین پسوردی تولید میکنه بدست بیاره و بعد اون رو با هشهای ثبت شده در دیتابیس مقایسه کنه.
پس، نتیجه میگیریم که تاوقتی هش ثابت بدست هکر نیافتاده، با داشتن تمام محتویات دیتابیس ما بازهم نمیتونه پسوردهای اولیهء کاربران رو کشف کنه.
نتیجه ای که حاصل میشه اینه که، نیازی نیست تمام سالت ها رو در جایی غیر از دیتابیس ذخیره کنیم. چون اگر جای امن تری برای ذخیرهء اطلاعات داشته باشیم، ذخیرهء فقط سالت ثابت در اون مکان برای ناتوان کردن هکر در کرک هش ها کافیه، و اگر هکر به اون مکان دسترسی پیدا کنه طبیعتا هردوی هش ثابت و هش های رندوم رو بدست خواهد آورد و وضعیت هیچ فرقی نمیکنه.

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

خب حالا میریم وضعیتی رو بررسی میکنیم که هکر هردوی دیتابیس و سالت ثابت رو بدست آورده.

در این صورت هکر میتونه هش پسورد love رو به ازای هر اکانت محاسبه کنه.
بطور مثال اگر سالت ثابت ما xmsopHrteyDss472 و سالت اکانت user1 برابر emroUaqw423XXmaqz باشه، هکر هش love رو به این شکل محاسبه میکنه:

echo md5('xmsopHrteyDss472'.'emroUaqw423XXmaqz'.'love') ;

که جوابش میشه: ce752ec7e18178e8b1182a37f60a11f7
حالا هکر (یا درواقع برنامهء خودکارش) این مقدار رو با هش user1 مقایسه میکنه. اگر هش یکسان بود یعنی پسورد کاربر love است و اگر نبود یعنی معلوم نیست.
اما حالا فرضا پسورد user2 دقیقا همین love باشه. آیا هش اون با این هش یکی هست؟
جواب واضحا خیر هست. چون سالت رندوم اون کاربر که در محاسبهء هش دخیل هست متفاوت است از هش کاربر اول که ما برای محاسبهء این هش بکار بردیم.
فرضا سالت رندوم user2 برابر wuoi344xhdpq0DDkL هست.
محاسبهء:

echo md5('xmsopHrteyDss472'.'wuoi344xhdpq0DDkL'.'love') ;

منجر به هش کاملا متفاوت e246b6bf787b18f4c045c88109b038ef میشه.

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

اما اگر سالت متفاوت به ازای هر کاربر رو نداشتیم، نتیجهء محاسبهء:

echo md5('xmsopHrteyDss472'.'love');

برابر 4272d5eaa7006541751d60380e8df0be میشد که هرکدام از اکانتها اگر پسورد love داشتن چنین هشی در رکورد دیتابیس اونها مشاهده میشد. چون تنها ورودی این هش، سالت ثابت و پسورد love هست که خروجی اون وابسته به اینکه روی اکانت کدام کاربر اون رو تست میکنیم نداره.

بنابراین اگر سالت رندوم به ازای هر کاربر وجود نداشت، و دیتابیس ما فرضا 1000 کاربر داشت، و هکر میخواست صدهزار پسورد رو روی هر اکانت چک کنه، و هر عملیات تولید هش روی رایانهء هکر 1 هزارم ثانیه طول میکشید، زمان مورد نیاز برای این حمله:

100000*0.001=100

100 ثانیه یا تقریبا یک و نیم دقیقه وقت لازم میبود.
چون نیاز بود هر هش فقط یک بار محاسبه بشه و به تعداد اکانتها وابسته نبود.
البته ما زمانهای لازم برای بقیهء عملیات و مقایسه و اینطور چیزها رو حساب نکردیم. چون فقط میخوایم تفاوت زمانی رو که سالت رندوم به ازای هر کاربر اضافه میکنه محاسبه کنیم.

خب حالا محاسبه درصورت وجود سالت رندوم به معنای اینه که هکر نیاز داره هش هرکدام از اون صدهزار پسورد رو به ازای هر اکانت مجددا تولید کنه.
در نتیجه زمان لازم برابر این میشه:

100000*0.001*1000=100000

و این یعنی هزاربرابر زمان بیشتر؛ صدهزار ثانیه نزدیک به 28 ساعت.

میبینید که در این مثال ساده اختلاف زمانی بسیار زیادی توسط استفاده از سالت رندوم بوجود آمد.
این حداقل خاصیتی هست که یک سالت رندوم به ازای هر کاربر داره.
این مقادیر اونقدری بزرگ و در عمل در خیلی موارد کاملا موثر هستن که استفاده از سالت رندوم رو توجیه بکنن.

شاید بگید زمان 1 میلی ثانیه برای یک هش md5 خیلی زیاده. بله، اما اولا ما از md5 استفاده نمیکنم و از توابع هش جدیدتر و امن تری که کندتر هستن استفاده میکنیم، دوما از روش key stretching هم استفاده میکنیم تا زمان لازم برای محاسبهء هر هش رو افزایش بدیم. در روش key stretching عملیات هش هزاران بار تکرار میشه.
بطور مثال این کدی هست که در یک بخش از phpass-0.3 برای همین کار بکار رفته:

$hash = md5($salt.$password);
do {
$hash = md5($hash.$password);
} while(--$count);

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

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

درسته بعضی هکرها میتونن از رایانه های زیاد یا منابع پردازشی برابر با هزاران رایانهء عادی استفاده کنن، اما بهرحال طول و تعداد پسوردهای قابل انتخاب اونقدری زیاده که اونا هم نمیتونن هش هر پسوردی رو کرک کنن. اگر کرک هر هشی به این سادگی بود که اینهمه الگوریتم و پروتکل رمزنگاری که بر مبنای هش عمل میکنن امنیت نداشتن و شکسته میشدن. کاری که شما با سالت و key stretching میکنید اینه که محدوده/تعداد پسوردهای قابل تست و کرک برای هکرها رو خیلی کوچکتر میکنید و بنابراین پسوردهای خیلی کمتری توسط هکرها قابل تست و کرک خواهند شد و در نتیجه یکسری پسوردهایی که قبلا اونقدری ضعیف بودن که با این حمله ها کشف بشن اما خیلی هم ضعیف نبودن که با چند هزار و چند صدهزار و چند میلیون برابر کردن پردازش مورد نیاز برای تست هر پسورد روی همهء اکانت ها کشف بشن، حالا در محدودهء کم و بیش امنی قرار میگیرن و هکر نمیتونه به تست کردن اونها برسه، چون قبلش باید میلیونها و میلیاردها ترکیب پسورد احتمالی دیگر رو با صرف زمان و هزینهء خیلی زیادی تست کنه. و این قضیه درمورد پسورد کاربران کاملا موثر هست، شما نمیتونید امنیت پسوردهای خیلی ضعیف رو به اطمینان بالایی برسونید که بگید قابل کرک نیستن (چون اینا رو با حدس و دیکشنری های کوچک از متداول ترین پسوردها یا Brute-force در محدوده های کوچک میشه کرک کرد)، اما امنیت پسوردهایی که اونقدرها هم ضعیف نباشن رو میتونید به حد بسیار خوبی برسونید. پسوردهای خیلی قوی هم که بهرصورت امن هستند، چون اینقدر تعداد حالتهایی ممکن برای رسیدن به اونها زیاده که بشر چنین قدرت پردازشی ای رو در اختیار نداره و تا آیندهء نزدیک هم پیشبینی نمیشه به چنین توان پردازشی ای برسه.

————————–

ما کاری کردیم که عملیات کشف پسورد اولیه از روی هش ها بسیار بسیار زمان‌برتر بشه. نمیشه گفت در تمام موارد تمام پسوردها غیرقابل نفوذ میشن.

طبیعتا یک پسورد خیلی ضعیف هنوز هم آسیب پذیره و ممکنه کشف بشه، ولی ما با ترکیب متد سالت رندوم به ازای هر اکانت و متد key stretching کاری کردیم که تعداد پسوردهایی که کرکرها میتونن تست کنن بسیار بسیار کمتر بشه. فرضا اگر قبلا اینقدر توان پردازشی و زمان مقرون به صرفه در اختیار داشتن که میتونستن حداکثر یک میلیارد پسورد رو روی اکانتهای کاربران ما تست کنن، الان با سیستم تقویت شدهء ما میتونن تنها ده هزار پسورد رو تست کنن. خلاصه حداکثر ممکن براشون هرچقدر باشه، با یک ضریب بسیار زیاد کاهش پیدا میکنه.

اگر قبلا به یک سال وقت برای کرک کردن یک دیتابیس مهم توسط یک حملهء Brute-force بسیار گسترده نیاز داشتن و این زمان براشون صرف میکرد، الان زمان مورد نیاز برای همون حمله صدهزار برابر شده!! و طبیعتا این خیلی خیلی موثر و اهمیتش غیرقابل تردیده. چون دیگه هکرها نمیتونن تعداد خیلی بالایی پسورد رو روی تعداد زیادی اکانت تست کنن و بنابراین پسوردهای خیلی کمتری کرک خواهند شد. پسوردهای واقعا ضعیف کرک میشن. مثل همون love که مثال زدیم. love فقط برای سادگی مثال و انجام محاسبات بود، نه اینکه بخوام بگم چنین پسورد درپیتی رو میشه با این سیستم ایمن کرد. نه یکسری پسوردهایی از این قویتر باید باشن. اکثر سیستمها پسورد کوتاهتر از 6 یا 8 کاراکتر رو قبول نمیکنن.

در همین مثالهایی که زدم و میتونن کاملا واقعی باشن، ما زمان لازم برای عملیات کرک رو صدهزار برابر افزایش دادیم!

البته نقش سالت ثابت هم در اینجا این بود که در هک هایی که هکر دیتابیس رو میخونه اما دسترسی به فایلها نداره، نمیتونه حمله ای رو جهت کرک کردن پسوردها صورت بده، چون بخشی از اطلاعات مورد نیاز جهت تولید هش ها رو در اختیار نداره. اما نباید فرض کرد که سالت ثابت به هیچ وجه قابل دسترسی هکرها نیست. وقتی سایت رو دیفیس میکنن یعنی دسترسی کافی دارن و میتونن فایل آپلود یا ویرایش کنن، پس میتونن وبشل کار بذارن یا اصلا با استفاده از همون PHP کدهای لازم برای خوندن سالت ثابت ما رو نوشته و اجرا کنن. اگر برنامهء ما این کار رو میکنه، برنامهء هکر هم میتونه، فقط هکر باید بتونه دسترسی نوشتنی به www پیدا کنه. اونوقت اگر ما سالت رندوم به ازای هر اکانت و key stretching نداشته باشیم، ظرفیت کرک هش ها توسط هکر بسیار زیادتر خواهد بود.

پاسخ دهید

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

*

شما می‌توانید از این دستورات HTML استفاده کنید: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>