در مقالۀ رمزنگاری چیست؟ به بررسی برخی مفاهیم اولیه در رابطه با رمزنگاری پرداختیم و در این مقاله قصد داریم تا انواع روشهای رمزگذاری پسورد به منظور ذخیرهسازی آنها در دیتابیس را معرفی کرده و نقاط ضعف و قوت هر یک را بیان کنیم.
در صنعت توسعۀ نرمافزار معمولاً احراز هویت کاربران بر اساس پسورد انجام میشود و نیاز به توضیح نیست که دولوپرها به منظور احراز هویت کاربران باید رمزعبور ایشان را در دیتابیس ذخیره کنند که روشهای متعددی برای ذخیرۀ پسورد و احراز هویت کاربران بر آن اساس وجود دارد که در ادامه هر یک از آنها را مورد بررسی قرار خواهیم داد.
ذخیرۀ پسورد به روش متن ساده و بدون رمزگذاری
سادهترین و در عین حال ناامنترین روش ذخیرۀ پسورد به منظور احراز هویت کاربران، ذخیرۀ آن به صورت اصطلاحاً Clear Text (متن ساده) در دیتابیس است که در آن احراز هویت کاربران با مقایسۀ پسورد ورودی توسط کاربر با رمزعبور هَشنشده که از قبل در دیتابیس ذخیره شده انجام میشود که این روش تحت هیچ عنوان توصیه نمیشود و در توسعۀ نرمافزار نیز ناامنترین روش به شمار میرود که در ادامه برخی از مشکلات آن را مورد بررسی قرار میدهیم.
وبمسترهای سایتهای مختلف به دیتابیس دسترسی دارند و چنانچه برخی از کاربران از یک پسورد مشابه برای تمامی اکانتهای فضای مجازی خود همچون ایمیل و شبکههای اجتماعی استفاده کنند، با این فرض که پسورد وی به صورت Clear Text در دیتابیس ذخیره شده باشد، وبمستر یا ادمین سایت به راحتی قادر بر دیدن پسوردهای رمزگذارینشده خواهد بود و این اصلاً خوب نیست چرا که ممکن است مورد سوءاستفاده قرار گیرد. به عنوان مثال داریم:
+----+-----+
| password |
+----+-----+
| Ali@1359 |
+----+-----+
همچنین اگر هکری به هر شکلی به دیتابیس دسترسی پیدا کند، تمامی پسوردهای کاربران را به صورت ساده و بدون رمزگذاری در اختیار خواهد داشت و از همین روی ذخیرۀ پسورد به این روش در دیتابیس اصطلاحاً یک Anti Pattern است (در صنعت توسعهٔ نرمافزار هر سولوشنی که نامؤثر و غیراصولی باشد Anti Pattern گفته میشود.)
ذخیرۀ پسورد به روش هشینگ سادۀ آن
پیادهسازی این روش نسبتاً آسان بوده اما از امنیت پایینی برخوردار است به طوری که پسورد کاربران به منظور احراز هویت ایشان با هَش فانکشنهایی همچون SHA-256 رمزنگاری شده و مستقیماً در دیتابیس ذخیره میشود و در هنگام لاگین هم پسورد ورودی توسط کاربر هَش شده و با مقدار هَش ذخیرهشدۀ متناظر آن در دیتابیس مقایسه میشود.
بهکارگیری این روش نیز به دلیل امنیت بسیار پایین آن توصیه نمیشود چرا که پسوردهای رمزگذاریشده با فانکشنهای هَشینگ در برابر حملاتی همچون Dictionary Attack آسیبپذیر هستند که در آن هکرها به دیتابیس اپلیکیشن مد نظر خود حمله میکنند و در صورتی که به آن دسترسی پیدا کنند، از لیستی حاوی چندین میلیون پسورد مختلف به همراه مقادیر هَش آنها استفاده کرده و بدین طریق احتمال دارد بتوانند برخی پسوردها را رمزگشایی میکنند (پروسۀ یافتن پسورد کاربران به روش Dictionary Attack بسیار سریع انجام میشود چرا که در این روش هر یک از مقادیر هَش موجود در لیست با پسورد هَششده مقایسه میشود که مقایسۀ ساده دو استرینگ است که نیاز به محاسبات پیچیدهای ندارد.)
ذخیرۀ پسورد به روش هَش کردن دیتا به همراه مقادیر Salt
پیادهسازی این روش در مقایسه با دو روش فوق پیچیدهتر بوده و برای ذخیرهسازی پسوردها نسبتاً امنتر است به طوری که پسوردهای هر یک از کاربران با مقادیری تحت عنوان Salt که به صورت رندوم تولید میشوند، ترکیب شده و مجموعۀ هر دو با هم هَش میشوند. سپس استرینگ رمزگذاریشده به همراه مقدار Salt بهکاررفته در آن در دیتابیس به صورت مجزا ذخیره میشود (Salt در لغت به معنی «نمک» است.) به طور خلاصه داریم:
salt + hash(password + salt)
به عبارتی، توسط فانکشنهای از پیش تعریفشده در تمامی زبانهای برنامهنویسی یک عدد تصادفی به عنوان Salt تولید میشود. سپس پسورد انتخابی توسط کاربر به مقدار Salt چسبانده شده و هر دوی آنها به عنوان پارامتر ورودی هَش فانکشن در نظر گرفته میشوند. از این پس، یک مقدار Salt داریم و یک مقدار Hash که باید هر دوی آنها را در ستونهای مجزایی در دیتابیس ذخیره ساخت:
+--------+-------------------+
| salt | hashed_password |
+--------+-------------------+
| 665894 | 74b5899a538410fdf |
+--------+-------------------+
در هنگام لاگین کاربر نیز به منظور چک کردن صحت پسورد ورودی، میتوان مقدار Salt متناظر با آن را از دیتابیس فراخوانی کرده سپس دیتای دریافتی را به پسورد ورودی کاربر چسباند و هر دو را با همان هَش فانکشن قبلی رمزگزاری کرد و در نهایت هم خروجی را با مقدار هَش ذخیرهشده در دیتابیس مقایسه کرد و در صورت یکسان بودن هر دو استرینگ، امکان لاگین کردن برای کاربر فراهم خواهد شد.
نقطۀ قوت این روش نسبت به دو روش قبل استفاده از مقادیر Salt رندوم و یونیک به همراه پسورد کاربر است که منجر به تولید پسوردهای هششدۀ منحصربهفردی میگردد زیرا استفاده از مقادیر Salt، که عددی کاملاً تصادفی است، به همراه پسورد کاربر و سپس هَش کردن آنها منجر به تولید مقادیر منحصربهفردی میشود.
در حقیقت، ذخیرۀ پسوردها به روش هَش کردن آنها به همراه مقادیر رندوم Salt امنتر از روشهای قبلی است اما در عین حال یکسری نقاط ضعف دارا است چرا که صرفاً از روش هَشینگ بهتری برای تأمین امنیت پسورد کاربران استفاده کرده است که یک لایۀ محافظتی برای پسورد کاربران به شمار رفته و دسترسی به آنها را کُندتر میسازد اما کماکان میتواند آسیبپذیر باشد.
ذخیرۀ پسورد به روش هَشینگ مبتنی بر KDF
KDF مخفف عبارت Key Derivation Function بوده و این هدف را دارا است تا یک ورودی تصادفی بگیرد و بر آن اساس یک اصطلاحاً Secret Key قوی یا به عبارتی یک پسورد تولید کند که پیادهسازی این روش پیچیدهتر بوده اما برای احراز هویت مبتنی بر رمزعبور از امنیت بالایی برخوردار است. به عنوان مثال داریم:
DK = KDF(key, salt, iterations)
در تفسیر فرمول فوق باید گفت که DK
کلید حاصله است و KDF
فانکشنی است که آن کلید را تولید میکند، key
پسورد انتخابی است، salt
یک عدد رندوم است و iterations
هم اشاره به یکسری پارامتر ورودی برای فانکشنهایی همچون Scrypt یا Argon2 میکند که هرچه بزرگتر باشد، جلوی حملاتی از جنس Brute Force بیشتر گرفته خواهد شد.
در هنگام لاگین کردن یک کاربر نیز مقدار Salt متناظر با آن از دیتابیس گرفته شده و همان فانکشن قبلی مورد استفاده برای هشینگ به همراه پارامترهای مد نظر به منظور هشینگ پسورد ورودی کاربر به کار گرفته میشود و در نهایت مقدار کلید حاصلشده با مقدار کلید ذخیرهشده در دیتابیس مقایسه میگردد که اگر دو مقدار با هم یکسان بودند، احراز هویت کاربر با موفقیت انجام شده است.
این روش در برابر اکثر حملات مقاوم بوده و به عنوان روشی استاندارد در صنعت توسعۀ نرمافزار محسوب میشود. همچنین امنیت این روش کاملاً به فانکشن مورد استفاده برای هَشینگ و پارامترهای ورودی همچون تعداد دفعات تکرار یا میزان حافظۀ مورد نیاز برای اجرای آن بستگی دارد.