هش کردن رمزهای عبور در جاوا — به زبان ساده

۳۷۵ بازدید
آخرین به‌روزرسانی: ۰۶ شهریور ۱۴۰۲
زمان مطالعه: ۵ دقیقه
هش کردن رمزهای عبور در جاوا — به زبان ساده

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

هش کردن چیست؟

هش کردن فرایندی است که در طی آن یک رشته یا Hash از یک پیام مفروض با استفاده از یک تابع ریاضی ساخته می‌شود. این تابع ریاضی به نام «تابع هش رمزشناختی» (Cryptographic Hash Function) نامیده می‌شود. با این که چند تابع هش هم اینک وجود دارند، اما توابعی که برای هش کردن رمزهای عبور طراحی شده‌اند باید چهار مشخصه زیر را داشته باشند تا امن محسوب شوند:

  • باید قطعی باشند، یعنی یک پیام که از سوی یک تابع هش پردازش می‌شود، همواره باید پیام هش شده یکسانی را تولید کند.
  • معکوس‌پذیر نباشند، یعنی امکان تولید یک پیام از روی هش آن وجود نداشته باشد.
  • آنتروپی بالایی داشته باشند، یعنی تغییرات کوچک در یک پیام، باید منجر به تفاوت‌های وسیعی در هش حاصل شود.
  • در برابر تصادم مقاومت داشته باشند؛ منظور از این گفته آن است که دو پیام متفاوت نباید هش یکسانی تولید کنند.

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

ضمناً، تابع‌های هش کردن رمزهای عبور باید کُند باشند، چون الگوریتم‌های سریع مستعد حملات brute force هستند که در طی آن مهاجمان تلاش می‌کنند یک رمز عبور را با هش کردن و مقایسه میلیاردها یا تریلیون‌ها رمز عبور بالقوه در طی هر ثانیه حدس بزنند.

برخی تابع‌های هش عالی که همه معیارهای فوق را دارند شامل PBKDF2 ،BCrypt و SCrypt هستند. اما ابتدا باید برخی الگوریتم‌های قدیمی‌تر را بررسی کنیم تا ببینیم چرا استفاده از آن‌ها دیگر توصیه نمی‌شود.

MD5: توصیه نمی‌شود

نخستین تابع هش که بررسی می‌کنیم، الگوریتم «چکیده پیام» (message-digest) به نام MD5 است که در سال 1992 توسعه یافته است. متد MessageDigest جاوا امکان محاسبه آن را ساده کرده است و هنوز در برخی موارد می‌تواند مفید باشد. با این وجود، در طی سال‌های اخیر ثابت شده است که MD5 مشخصه چهارم هش کردن رمزهای عبور را که در بخش قبلی اشاره کردیم ندارد. چون از نظر محاسباتی تولید تصادم بسیار راحت است. علاوه بر آن MD5 یک الگوریتم سریع است و از این رو در برابر حمله‌های brute-force کارایی ندارد.

SHA-512: توصیه نمی‌شود

در این بخش نگاهی به SHA-512 خواهیم داشت که بخشی از خانواده الگوریتم هش امن است. این خانواده با معرفی SHA-0 در سال 1993 آغاز شده است.

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

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

پیاده‌سازی در جاوا

در این بخش به بررسی پیاده‌سازی الگوریتم هش کردن SHA-512 در جاوا می‌پردازیم. ابتدا باید مفهوم Salt را درک کنیم. Salt به بیان ساده یک دنباله تصادفی است که برای هر هش جدید تولید می‌شود.

با استفاده از این خصوصیت تصادفی بودن میزان آنتروپی هش بالا می‌رود و پایگاه داده خود را در برابر لیست‌های از قبل تهیه شده از هش‌ها که به نام «جداول رنگین‌کمان» (rainbow tables) شناخته می‌شوند مقاوم‌تر می‌سازیم. تابع هش جدید ما به طور تقریبی به صورت زیر است:

1salt <- generate-salt;
2hash <- salt + ':' + sha512(salt + password)

تولید یک Salt

برای استفاده از Salt از کلاس SecureRandom از java.security استفاده می‌کنیم:

1SecureRandom random = new SecureRandom();
2byte[] salt = new byte[16];
3random.nextBytes(salt);

سپس از کلاس MessageDigest برای پیکربندی تابع SHA-512 با Salt خود کمک می‌گیریم:

1MessageDigest md = MessageDigest.getInstance("SHA-512");
2md.update(salt);

بدن ترتیب اینک می‌توانیم از متد digest برای تولید رمز عبور هش شده جدید خود بهره بگیریم:

1byte[] hashedPassword = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));

چرا استفاده از آن توصیه نمی‌شود؟

حتی زمانی که از Salt استفاده می‌کنیم، SHA-512 همچنان یک گزینه متوسط محسوب می‌شود، اما گزینه‌های قوی‌تر و کُندتری نیز وجود دارند. ضمناً گزینه‌های دیگر که در ادامه بررسی خواهیم کرد، یک ویژگی مهم به نام «قدرت قابل پیکربندی» (Configurable Strength) دارند.

PBKDF2 ،BCrypt و SCrypt

PBKDF2 ،BCrypt و SCrypt سه الگوریتم هش کردن هستند که استفاده از آن‌ها توصیه می‌شود.

چرا باید از این الگوریتم‌ها استفاده کنیم؟

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

پیاده‌سازی PBKDF2 در جاوا

اکنون Salt-ها مفهومی بنیادی در زمینه هش کردن رمزهای عبور محسوب می‌شوند و از این رو باید یکی از آن‌ها را برای PBKDF2 نیز استفاده کنیم:

1SecureRandom random = new SecureRandom();
2byte[] salt = new byte[16];
3random.nextBytes(salt);

سپس یک PBEKeySpec و یک SecretKeyFactory ایجاد می‌کنیم که با استفاده از الگوریتم PBKDF2WithHmacSHA1 وهله‌سازی می‌شوند:

1KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
2SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

پارامتر سوم (65536) به طرز مؤثری پارامتر قدرت است. این پارامتر تعیین می‌کند که این الگوریتم برای چند بار تکرار خواهد شد و بدین ترتیب زمان مورد نیاز برای تولید هش افزایش می‌یابد. در نهایت می‌توانیم از SecretKeyFactory برای تولید هش استفاده کنیم:

1byte[] hash = factory.generateSecret(spec).getEncoded();

پیاده‌سازی BCrypt و SCrypt در جاوا

تا به این جا مشخص شد که BCrypt و SCrypt هنوز در جاوا پشتیبانی نمی‌شوند؛ اما برخی کتابخانه‌های جاوا از آن‌ها پشتیبانی می‌کنند. یکی از این کتابخانه‌ها Spring Security نام دارد.

هش کردن رمزهای عبور با Spring Security

با این که جاوا به صورت بومی از هر دو الگوریتم هش کردن PBKDF2 و SHA پشتیبانی می‌کند، اما الگوریتم‌های BCrypt و SCrypt همچنان پشتیبانی نمی‌شوند.

خوشبختانه کتابخانه Spring Security از طریق اینترفیس PasswordEncoder از هر سه این الگوریتم‌های توصیه شده پشتیبانی می‌کند، بنابراین:

  • MessageDigestPasswordEncoder الگوریتم‌های MD5 و SHA-512 را ارائه می‌کند.
  • Pbkdf2PasswordEncoder الگوریتم PBKDF2 را ارائه می‌کند.
  • BCryptPasswordEncoder الگوریتم BCrypt را ارائه می‌کند.
  • SCryptPasswordEncoder الگوریتم SCrypt را ارائه می‌کند.

انکودرهای رمز عبور برای PBKDF2 ،BCrypt و SCrypt همگی از پیکربندی قدرت مطلوب هش رمز عبور پشتیبانی می‌کنند. حتی بدون وجود یک اپلیکیشن مبتنی بر Spring Security می‌توان از این انکودرها به صورت مستقیم استفاده کرد. همچنین اگر از سایت خود با استفاده از Spring Security حفاظت می‌کنید در این صورت می‌توانید انکودر رمز عبور مطلوب خود را از طریق DSL آن یا از طریق تزریق وابستگی پیکربندی کنید.

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

نتیجه‌گیری

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

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
baeldung
۱ دیدگاه برای «هش کردن رمزهای عبور در جاوا — به زبان ساده»

سلام این رو متوجه شدم و کار میکنه اگر بخوام رمز گشایی کنم باید چکار کنم ?

نظر شما چیست؟

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