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

چند روزه درگیر بحث و کار خفن ناکی هستیم برای ایجاد یک تابع رندوم امن:
http://www.frostjedi.com/phpbb/viewt…p?f=46&t=30875

روی تابع crypt_rand کار کردم و یکسری ترفندها و اصلاحات مفید روش انجام دادم.
حالا کد تغییریافته خود تابع crypt_rand بماند که احتمالا بعدا در یک تاپیک جداگانه درج و روش بحث میکنیم؛ فعلا سیستم جمع آوری آنتروپی ای رو که تا اینجا بهش رسیدم تشریح میکنم براتون اگر نظری داشتید بگید:

در هر صفحهء اصلی پروژه (مثلا صفحهء فرم لاگین، صفحه ثبت نام و غیره) کدی گذاشتم به این شکل تا آنتروپی پارامترهای مختلف بازدیدکنندگان رو جمع آوری کنه:

$entropy=sha1(microtime().$pepper.$_SERVER['REMOTE_ADDR'].$_SERVER['REMOTE_PORT'].
$_SERVER['HTTP_USER_AGENT'].serialize($_POST).serialize($_GET).serialize($_COOKIE));

البته pepper در این بین یک رشتهء hard-code شده هست در کد منبع برنامه. این رشته رو موقع نصب برنامه میتونیم بصورت دستی تعیین کنیم تا به امنیت کمک بشه (نکته: گرچه با بحث فعلی بی ربط هست ولی این رشته در تولید هش پسوردها هم دخالت داره و در اصل برای اون منظور درنظر گرفته شده بود).

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

این کدها آخرین و پیشرفته ترین ترفندهایی هستن که تاحالا تونستم با پرس و جو و تحقیق و تفکر بهشون برسم. بقول معروف state of the art :D

خب همونطور که میبینید ما آنتروپی تمام این پارامترها رو با استفاده از یک تابع هش مثل SHA1 با هم مخلوط کردیم که این یکی از کاربردهای جالب و مفید توابع هش رمزنگاری هست بخاطر غیرقابل پیشبینی بودن و غیرقابل معکوس کردن خروجی اونها.
البته این از طرف دیگر باعث میشه حجم دیتایی که باید ذخیره کنیم همیشه یک مقدار ثابت و محدود باشه که درمورد SHA1 برابر 160 بیت است. این از نظر ساده شدن منطق و الگوریتم و محدود و تثبیت کردن حجم ذخیره سازی خوبه اما از طرف دیگر باعث میشه که آنتروپی جمع آوری شده در سیستم هیچوقت نتونه بیشتر از 160 بیت بشه (ولو آنتروپی موجود در درخواستهای بازدیدکنندگان خیلی بیشتر باشه)، ولی بهرصورت فکر نمیکنم این به هیچ وجه مشکلی باشه و این میزان آنتروپی واقعی برای بیشتر کاربردهای وب کافی بنظر میرسه.
ما این آنتروپی رو با خروجی یک الگوریتم PRNG (مولد اعداد شبه تصادفی) یا یک الگوریتم CSPRNG (مولد اعداد شبه تصادفی امن) یا یک منبع اعداد رندوم مثل /dev/urandom مجددا بوسیلهء یک تابع هش ترکیب میکنیم تا به یک خروجی با آنتروپی و در نتیجه امنیت بالاتر دست پیدا کنیم.
یک الگوریتم PRNG مثل تابع mt_rand است که بیش از حد ضعیفه و بنابراین اساسا به چنین روشها و آنتروپی افزوده ای نیاز داریم تا امنیت رو حرفه ای کار کرده باشیم.
حتی یک الگوریتم CSPRNG هم اگر منبع seed اولیه با آنتروپی و امنیت بالایی نداشته باشه نمیتونه معجزه کنه و بنابراین ما به امنیت خروجی این الگوریتم ها هم با این کار اضافه میکنیم.
و سرانجام منابعی مثل dev/urandom و حتی /dev/random هم ضرری نداره اگر آنتروپی افزودهء ما به خروجیشون اضافه بشه، گذشته از اینکه خود این منابع هم خیلی وقتها با کمبود آنتروپی واقعی و امن مواجه هستن (چون عمدتا از منابع محدود سخت افزاری و پارامترهای داخلی سیستم عامل استفاده میکنن) و درمورد اونها ریسک امنیتی ممکنه موجود باشه.

خب ما چطور آنتروپی بازدیدکنندگان مختلف رو با هم ترکیب و ذخیره میکنیم؟
به این شکل:

$query="update `crypto` set `value`=sha1(concat(`value`, '$entropy')) where name='entropy'";

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

ببخشید دیگه این بهرحال نیاز به یک عملیات دیتابیس به ازای هر درخواست داره و این هزینه ای هست که باید برای امنیت و جمع آوری آنتروپی ارزشمند بپردازیم.
البته یکی از اساتید (توسعه دهندهء کتابخانهء phpseclib) استفاده از سشن رو پیشنهاد میکنن (*) تا به دیتابیس وابسته نباشیم (فکر میکنم بیشتر از نظر وابستگی میگه تا پرفورمنس)، ولی چون امنیت و ثبات فایل سشن درکل پایین تر از ذخیره در دیتابیس هست بنده ترجیح دادم از دیتابیس استفاده کنم. ولی اگر در عمل مشکل پرفورمنس دیده شد شاید به استفاده از سشن روو بیاریم. البته باید بگم نرم افزار فروم phpbb هم آنتروپی محدودی رو که به شکل دیگری از بازدیدها بدست میاره در دیتابیس ذخیره میکنه (منبع: http://phpxref.com/xref/phpbb/includ…urce.html#l142).

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

تا اینجا مطلب مربوط به جمع آوری آنتروپی میشد، اما بیشتر هم توضیح میدم تا طرز استفادهء عملی رو متوجه بشید و مطلب بیشتر براتون روشن بشه.

خب هروقت ما خواستیم اعداد رندم امن تولید کنیم یک نسخه از این آنتروپی رو از دیتابیس دریافت میکنیم:

$query="select `value` from `crypto` where `name`='entropy'";

خب بعد ما میتونیم از این آنتروپی به شکلی شبیه این استفاده کنیم:

extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));

الان با این کد ما یک عدد صحیح رندوم میگیریم (خروجی در متغییری بنام random ذخیره خواهد شد).
توجه کنید که ما آنتروپی رو مستقیما بعنوان seed به یک PRNG مثل mt_rand نمیدیم، چون به این شکل آنتروپی ما عملا هدر میره (عمدتا بخاطر محدودیت محدودهء seed این تابع که حداکثر 64 بیت است، و در درجه های بعدی احتمالا بخاطر بقیهء ضعف هایی که داره). اما اگر یک CSPRNG داشتیم میتونستیم آنتروپی خودمون رو مستقیما بعنوان seed بهش بدیم و در اون صورت حتی میتونست از این متد sha1 که در این کد بکار رفته بهتر باشه. بنظر بنده sha1 در اینجا کم و بیش نقش یک CSPRNG رو ایفا میکنه. میشه گفت mt_rand فقط داره یک مقدار کمی آنتروپی و شاید کمی خواص امنیتی دیگه به خروجی اضافه میکنه. چون mt_rand هم عملا از زمان و PID استفاده میکنه که ما فقط PID رو در پارامترهای آنتروپی خودمون وارد نکردیم (که بنظرم اگر بخوایم میتونیم وارد کنیم و اونوقت شاید اصلا به mt_rand نیازی نباشه، اما بهرحال بخاطر احتیاط بیشتر ازش استفاده میکنیم).
راستی بازهم یک microtime که در کد بالا میبینید به بقیهء پارامترها اضافه شده چون آنتروپی جمع آوری شده از بازدیدکنندگان رو یک بار در هر درخواست از دیتابیس واکشی میکنیم و بنابراین بعدش در کل طول اجرای اسکریپت ثابته، اما microtime میتونه در هر بار فراخوانی تابع رندوم و تولید یک عدد رندوم متفاوت باشه و این نویز در زمانهای اجرا خودش میتونه یک منبع آنتروپی مفید باشه برای امنیت بیشتر.

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

—————————

در اینجا این سیستم جمع آوری آنتروپی رو مطرح کردم و روش بحث شده:
http://stackoverflow.com/questions/9…random-numbers

در مجموع باید بگم بنظر خودم این سیستم برای کاربردهای معمولی بقدر کافی کارا و امنه و معقوله. یعنی بعد از تمام تحقیقات و تحلیل ها و تبادل نظرها.
توسعه دهندهء کتابخانهء امنیتی و رمزنگاری phpseclib هم که تاییدش کرد.

ضمنا کسانی که استفاده از منابع آنلاینی مثل Random.Org رو توصیه میکنن باید بدونن این منابع از نظر اصول منطق و علم رمزنگاری اونقدرها هم امن و توجیه شده نیستن، گذشته از هزینه ها و مشکلات احتمالی فنی که میتونن داشته باشن.
در این ارتباط در این بحث استدلالها و اطلاعات خوبی بیان شده:
http://crypto.stackexchange.com/ques…solu/1628#1628

حتی خودشون هم به این امر اذعان دارن: http://www.random.org/faq/#Q2.4

پاسخ دهید

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

*

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