ذخیره ایمن رمز عبور در دیتابیس

در این مطلب سعی می‌کنیم روش‌های متداول ذخیره سازی رمز عبور در دیتابیس را با هم مرور کنیم و معایب و مزایای هر یک را بررسی کنیم. مطالبی که در ادامه خواهید خواند مستقل از زبان برنامه‌نویسی نگاشته شده و بیشتر تئوری‌های امنیت در ذخیره‌سازی اطلاعاتِ استراتژیک را مدنظر قرار داده است.

به‌منظور سادگی، از زبان PHP برای نمایش توابع و کدها استفاده‌شده است.

روش بد: متنِ آشکار (Plain Text)

این‌یک قاعده‌ی بنیادی است.  به‌هیچ‌وجه رمزهای عبور را به‌صورت متنِ آشکار در بانک اطلاعاتی ذخیره نکنید:

نام کاربریرمز (Plain Text)
member1@islet.irpassword1_2
member2@islet.irpassword1_2

این قانون شامل حال نرم‌افزارهای کاربردی کم‌اهمیت، مثل پروژه‌های دانشگاهی، یا وب‌سایت ساده‌ی داخلی که در جمع دوستانتان برپا کرده‌اید هم می‌شود. به یاد داشته باشید که افراد اغلب از یک رمز در سامانه‌های دیگر هم استفاده می‌کنند. رمز عبوری که در وب‌سایت دانشجویی شما استفاده‌شده، ممکن است در سیستم بانکی آن فرد هم بکار رفته باشد. کشف یا سرقت رمز در سیستم شما ممکن است موقعیتشان در سامانه‌های دیگر را نیز به خطر اندازد!

اگر تصور می‌کنید که امکان ندارد کسی از این روش برای ذخیره‌ی رمزهای عبور استفاده کرده باشد، باید بگویم که کمپانی سونی در سال ۲۰۱۱ این اشتباه را مرتکب شده بود.

روش بد: درهم سازی رمز عبور (Hashing)

وشی بهتر برای ذخیره‌سازی ایمن رمزهای عبور در دیتابیس ، درهم سازی یک‌سویه (One-Way Hash) رمز عبور و سپس ذخیره آن است. به این مفهوم که از توابعی عمومی و فراگیری چون MD5 و SHA1 برای پنهان‌سازی رمزها استفاده کنیم. توابع در هم سازی، توابعی هستند که یک مجموعه داده‌ی بزرگ را به یک مجموعه داده‌ی با طول ثابت، نگاشت می‌کنند.

در این روش، بااینکه سرویس‌دهنده رمزها را به‌صورت متنِ آشکار ذخیره نمی‌کند ولی می‌تواند در فرآیند اعتبار سنجی کاربر از مقادیر درهم سازی شده استفاده کند. کافی است از همان تابعِ درهم سازی که در هنگام ذخیره‌ی رمزها از آن بهره برده است، استفاده کند:

چک کردن رمز عبور

این راه‌حل مقداری امن‌تر از نگهداری رمزها به‌صورت متنِ آشکار است. زیرا از دیدگاه تئوری نباید راهی برای معکوس سازی یک تابع درهم سازی یک‌سویه وجود داشته باشد. ولی متأسفانه هکرها مدت‌هاست راه‌حل را یافته‌اند!

مشکل اینجاست که اغلب توابع درهم سازی یک‌سویه (مانند MD5 و SHA1)، اصولاً یک‌سویه نیستند و متخصصان توصیه می‌کنند که به‌هیچ‌وجه از آن‌ها برای درهم سازی داده‌های حساس استفاده نشود. بجای آن می‌تواند از توابع قدرتمندتر و ایمن‌تری مانند SHA256 که حداقل تابه‌حال موردحمله شناخته‌شده‌ای قرار نگرفته است، استفاده کرد.

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

الگوریتم رمز عبور

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

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

خبر بد این است که کاربرانی که رمزهای عمومی و ساده مانند “password123” یا “passw_123” و هزاران گونه شبیه این‌ها را انتخاب می‌کنند به‌شدت در معرض خطر هستند. بااین‌حال درصورتی‌که یک رمز پیچیده ۱۶ کاراکتری که ترکیبی از اعداد و حروف تصادفی است برای خود انتخاب کنید، احتمالاً کمی در حاشیه‌ی امنیت قرار خواهید گرفت.

همچنین توجه کنید که در کد فوق، هکر به شکل مؤثری تمامی رمزهای به سرقت رفته را به‌یک‌باره موردحمله قرار می‌دهد به‌عبارت‌دیگر اصلاً مهم نیست که شما در بانک اطلاعاتی خود ده کاربر داشته باشید یا ده میلیون کاربر، تأثیر منفی چندانی در عملیات هکر برای کشف رمز کاربران ایجاد نخواهد کرد. چیزی که اینجا اهمیت خواهد داشت، سرعت  عمل هکر در گردش بین شاه‌کلیدهایش است! در حقیقت بالا بودن حجم کاربران شما به نفع هکر خواهد بود، زیرا احتمال وجود یک رمز ساده و عمومی در میان این حجم از اطلاعات، خودبه‌خود بالا می‌رود.

شاید تصور کنید، خب، بااین‌همه مشکل چه کسی از این توابع برای نگهداری رمزها استفاده خواهد کرد! تابع SHA1، تابعی است که لینکدین برای ذخیره رمز عبور کاربرانش در بانک اطلاعاتی استفاده کرده بود و در سال ۲۰۱۱ مجموعه ی بزرگی از این رمزها دزدیده و منتشر شد.

به‌طور خلاصه، ذخیره‌ی داده‌های درهم سازی شده با یک تابع ساده بدون استفاده از salt، ایمن نیست. درصورتی‌که هکر به شکلی به بانک اطلاعاتی شما دسترسی پیدا کند، بخش اعظمی از رمزهای عبور در معرض خطر خواهند بود.

روش بد: درهم سازی با salt ثابت

روش دیگر استفاده از توابع درهم سازی به همراه افزودن کمی نمک به رمز عبور است! به این مفهوم که قبل از درهم سازی، یک‌رشته‌ی پیچیده  و تصادفی را به رمز موردنظر چسبانده و سپس رشته حاصل را درهم سازی و ذخیره می‌کنیم:

نام کاربریsha1(“saltString” + password)
member1@islet.irc467b644150eb350bbc1c8b44b21b08af99268a2
member2@islet.irf1a770fd38fee6f1f8b3145562ba9613920dfea0

درصورتی‌که هکر به این رشته‌های درهم سازی شده بدون اطلاع از رشته salt، دسترسی پیدا کند، فرآیند رمزگشایی بسیار دشوارتر خواهد بود. به‌هرروی اگر هکری توانسته باشد به سرویس‌دهنده شما نفوذ کند قطعاً به منابع کد شما نیز به شکلی دسترسی خواهد یافت، که درنهایت به کشف رشته slat منجر می‌شود.

بااینکه استفاده از salt خیلی ایمن نیست ولی به‌کلی استفاده از جداول رنگین‌کمان را بی‌فایده خواهد کرد. زیرا در الگوریتم جداول رنگین‌کمان، رشته اضافی salt پیش‌بینی‌نشده است. به‌هرحال همان‌طور که گفته شد امروزه از جداول رنگین‌کمان به علت بالا رفتن سرعت رایانه‌ها دیگر استفاده نمی‌شود. هکر با کشف رشته salt می‌تواند برای رمزگشایی به شکل زیر عمل کند:

هک رمز عبور

به‌طور خلاصه، افزودن رشته salt ثابت کمک زیادی به امنیت داده‌ها نمی‌کند.

روش بد: درهم سازی با salt ویژۀ هر کاربر

روش دیگر که یک پله امن‌تر به نظر می‌رسد، ایجاد یک ستون در بانک اطلاعاتی و جدول کاربران است که یک‌رشته منحصربه‌فرد و  ویژه برای هر کاربر را در خود ثبت می‌کند. رشته salt به‌صورت تصادفی درزمانی که کاربر برای بار اول ایجاد می‌شود، ساخته و به همراه داده‌های کاربر ذخیره می‌شود.

نام کاربرsaltsha1(slat + password)
member1@islet.irc13de4۱b74404cb166dd60041dbf694e5c2ec0e7d15b42
member2@islet.iradf40af33ab75f29a9cf3f70d3fd14a7f477d752e9c550

فرآیند اعتبار سنجی کاربر خیلی پیچیده‌تر از گذشته نیست:

با استفاده از تکنیک slat منحصربه‌فرد به ازای هر کاربر، یک مزیت فوق‌العاده‌ای را کسب خواهیم کرد. هکر دیگر نمی‌تواند همه‌ی رمزهای کاربران را به‌یک‌باره موردحمله قرار دهد در عوض همان‌طور که در کد زیر مشاهده می‌کنید، مجبور خواهد بود که آن‌ها را تک‌تک مورد آزمایش قرار دهد:

هک

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

مشکل اساسی تمام روش‌های فوق سرعت اجرای توابعی مانند MD5 یا SHA1 است. این توابع با سرعتی بیش از ۱۰۰ میلیون بار در ثانیه بر روی رشته‌های رمز قابل‌اجرا هستند. بااینکه در طراحی این توابع ویژگی‌های امنیتی هم مدنظر بوده‌ اما سرعت‌ بالا (به‌ویژه برای کار با فایل‌های بزرگ و حجیم) نیز از مهم‌ترین ویژگی‌های آن‌ها است.

به‌طور خلاصه می‌توان نتیجه گرفت که اصولاً توابع درهم‌ساز یک‌سویه، برای ذخیره‌ی رمزهای عبور طراحی نشده‌اند.

روش خوب: استفاده از bcrypt

در مقابل توابع سریع درهم‌ساز، مجموعه‌ای از توابع که به‌صورت تخصصی برای رمزها بکار می‌روند نیز وجود دارد. این توابع علاوه بر یک‌سویه بودن، برای کُند بودن طراحی ‌شده‌اند!

یکی از این توابع، تابع Bcrypt است. هر بار اجرای تابع bcrypt، به ۱۰۰ میلی‌ثانیه زمان نیاز دارد که ۱۰،۰۰۰ بار کندتر از تابع sha1 است. ۱۰۰ میلی‌ثانیه برای آزمون صحت رمز کاربر در هنگام ورود، زمان زیادی نیست ولی برای هکری که می‌خواهد عمل کشف رمز را برای یک لیست بلند انجام دهد، به‌اندازه کافی زمان‌گیر و دشوار خواهد بود. به‌طور مثال اگر هکر بخواهد تابع bcrypt را برای یک لیست یک‌میلیاردی از رمزهای احتمالی پردازش کند به ۳۰،۰۰۰ ساعت (به ازای هر پردازنده) و هزینه‌ای در حدود ۱۲۰۰، آن هم فقط برای کشف یک رمز نیاز خواهد داشت. قطعاً کشف رمز بازهم غیرممکن نیست ولی این بار کمتر هکری علاقه‌مند به انجام آن خواهد بود.

نکته اصلی در این است که این تابع، دستورات ویژه‌ی درهم سازی و رمزنگاری خود را در یک حلقه و به تعداد زیاد اجرا می‌کند. از طرفی تابع bcrypt قابل پیکربندی است. به این مفهوم که با تغییر مقدار پارامتر cost، تعداد دفعات درهم سازی قابل تنظیم است. حتی اگر کمپانی اینتل به‌صورت غیرمنتظره و ناگهانی، رایانه‌ای ۱۰۰۰ برابر سریع‌تر از رایانه‌های امروزی روانه بازار کند، تنها کافی است مقدار cost را ده برابر بیشتر از قبل تنظیم کنید (رقم cost به‌صورت لگاریتمی محاسبه می‌شود)، که در این صورت کاری از رایانه‌ی سریع تازه‌وارد نیز ساخته نخواهد بود!

به علت کُند بودن الگوریتم bcrypt، شاید به نظر برسد که جداول رنگین‌کمان دوباره می‌توانند مورداستفاده هکرها قرار گیرند، ولی خوشبختانه درون تابع bcrypt قابلیت salt منحصربه‌فرد، جاسازی‌شده است. حتی با استفاده از برخی تنظیمات می‌توانید salt را در همان رشته درهم سازی شده نیز قرار دهید، بنابراین حتی نیاز به ساخت ستون جدیدی برای نگهداری salt هر کاربر هم نخواهید داشت. کد زیر نحوه‌ی استفاده از تابع را نمایش می‌دهد:

رمز عبور هش

اجازه دهید کمی بیشتر رشته‌ی درهم سازی شده‌ی فوق را بررسی کنیم

$۲y$11$L0ESbUqzpEavC1jvad0QP.kWL2rkCiRBPhpzcVOmL9QGRYXm21e3e

ساختار رشته دارای چهار بخشِ به هم چسبیده است. از چپ به راست شامل: نوع الگوریتم، شاخص هزینه (سختی)، رشته salt تصادفی، رمز هش شده!

$۲<a/b/x/y>$[cost]$[22 character salt][31 character hash]

همان‌طور که می‌بینید، هر دو مقدار salt و رمز در هم سازی شده، باهم در خروجی نمایان شده‌اند. همچنین مقدار cost یا پیچیدگی مورداستفاده برای درهم سازی را هم می‌توانید در خروجی مشاهده کنید.

فرآیند اعتبار سنجی کاربر در هنگام ورود، بسیار ساده است:

رمز عبور چک

در زبان PHP، توابع bcrypt توسط تابع پوششی password_hash پیاده‌سازی شده‌اند. تابع password_verify به‌صورت هوشمند مقدار salt را از رشته‌ی ارجاع داده‌شده در آرگومان دوم استخراج و توسط آن رمز ورودی را درهم سازی و با رمز کاربر مقایسه می‌کند.

سخن پایانی

اگر کاربران شما از رمزهای ساده و عمومی مانند “password” استفاده کرده باشند، صدها بار درهم سازی و استفاده از توابع مختلف رمزنگاری هم نمی‌تواند آن‌ها را از خطر دور نگاه دارد.هکرها همیشه در ابتدا، کلمات ساده‌تر را امتحان می‌کنند، پس اگر رمز شما ازنظر سادگی در بالای لیست آن‌ها قرار داشته باشد، برنده هکر خواهد بود.

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

متأسفانه رمزهای پیچیده نیز به‌سختی در حافظه افراد باقی می‌مانند. اگر چنین مشکلی ندارید، پیشنهاد می‌کنم از یک رمز عبور ۱۶ کاراکتری که به‌صورت تصادفی از ترکیب اعداد و حروف لاتین تولیدشده است، استفاده کنید. برخی پیشنهادهای جالب‌توجه مانند استفاده از رمزجمله‌ها نیز مطرح هستند.

👋

3 پاسخ

  1. ممنون از مطلب امنیت رمزهای عبور در دیتابیس

  2. بسیار عالی. واقعا تابع مذکور نسبت به روشهای مشابه بسیار قویتر و امن تر است.
    بطور خاص برای PHP تابع password_hash از نسخه ۵.۵ به بالا پشتیبانی میشه. برای نسخه ۵.۳ به بالا این کتابخانه را پیشنهاد میکنم https://github.com/ircmaxell/password_compat

    1. ممنون رضا 🙂

      فکر میکنم بهتره اصلا از PHP های زیر ۵٫۶ استفاده نشه به طور کل. به خاطر مشکلات امنیتی که بعضا دارند و اینکه دیگه پشتیبانی نمیشن متاسفانه!

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

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