Salting در امنیت چیست؟ توضیح هش کردن رمز عبور
در ژوئن ۲۰۱۲، یک مهاجم ۱۱۷ میلیون رکورد حساب کاربری لینکدین را در یک انجمن روسی منتشر کرد. این لیست، دیواری از هشهای SHA-1 بدون نمک بود. SHA-1 سریع است. بدون نمکی برای شکستن رمزهای عبور یکسان، هش یکسان هزاران بار در کنار هش یکسان قرار میگرفت. تقریباً در عرض هفتاد و دو ساعت، محققان امنیتی حدود نود درصد از فایل را رمزگشایی کردند. هنگامی که این نقض امنیتی در ماه مه ۲۰۱۶ به عنوان بخشی از یک مجموعه داده ۱۶۷ میلیون رکوردی دوباره ظاهر شد، این اعتبارنامهها سالها به کمپینهای جعل اعتبارنامهها کمک کردند.
دفاعی که میتوانست آن نقض امنیتی را به یک پاورقی تبدیل کند - یعنی اضافه کردن نمک به هر رمز عبور ذخیره شده - در سال ۱۹۷۹ وجود داشت. به آن نمکپاشی میگویند. این کار جدید، گران و چندان هوشمندانه نیست. این تفاوت بین نشت اطلاعات پایگاه داده به عنوان یک حادثه مهار شده و مشکل رمز عبور همه برای نیم دهه است.
بیشتر شکستهای ذخیرهسازی رمز عبور مدرن هنوز هم از یک ریشه ریشهای ناشی میشوند: کسی تصمیم گرفته که «ما از SHA-256 استفاده کنیم» کافی است. این مقاله به بررسی این موضوع میپردازد که Salting واقعاً چیست، در عمل چگونه کار میکند، در برابر چه چیزهایی محافظت میکند و چه چیزهایی محافظت نمیکند، و OWASP چه چیزی را به عنوان پیشفرض سال 2026 توصیه میکند.
نمک زدن در امنیت و هش کردن رمز عبور چیست؟
موضوع را به یک جمله خلاصه کنید. Salting به معنای اضافه کردن دادههای تصادفی به یک رمز عبور قبل از هش شدن آن رمز عبور است. مقدار تصادفی - Salt - یک بار برای هر حساب کاربری تولید میشود، زمانی که Salt در کنار هش رمز عبور جدید تولید میشود، در فضای خالی کنار هش حاصل ذخیره میشود و در هر ورود دوباره خوانده میشود. این یک کلید نیست. یک راز نیست. رمزگذاری نیست. یک اصلاحکننده عمومی با دقیقاً یک کار: اطمینان از اینکه وقتی دو کاربر رمز عبور یکسانی دارند، هرگز رمز عبور ذخیره شده هش شده یکسانی را به اشتراک نگذارند.
هش کردن به خودی خود یک طرفه است. اگر یک رمز عبور را از طریق SHA-256 یا Argon2id وارد کنید، یک مقدار با طول ثابت دریافت میکنید که هیچ الگوریتمی نمیتواند آن را معکوس کند. نکته این است که «هیچ الگوریتمی» یک میانبر ارزان را حذف نمیکند - جستجوی هش در یک فرهنگ لغت از پیش محاسبه شده. نمک زدن آن میانبر را میبندد. اضافه کردن نمک به فرآیند هش کردن، یک هش منحصر به فرد برای هر کاربر ایجاد میکند، حتی زمانی که متن رمز عبور اصلی آنها کلمه به کلمه یکسان باشد. رمزهای عبور رایج مانند "password" و "qwerty" دیگر در پایگاه داده با هم برخورد نمیکنند. نمک زدن تضمین میکند که نشت یک ردیف چیزی در مورد شخص دیگری به مهاجم نمیگوید.
سه قانون، salt قابل استفاده را از salt بیفایده جدا میکند. قانون اول: هر رمز عبور، salt تصادفی مخصوص به خود را دریافت میکند که به تازگی هنگام ثبتنام یا تنظیم مجدد حساب کاربری توسط کاربر تولید میشود. یک رمز عبور salt شده که در حسابهای مختلف استفاده میشود، به سختی بهتر از یک رمز عبور unsalt شده است - حمله گروهی به همین روش عمل میکند. قانون دوم: salt ممکن است عمومی باشد. دانستن salt به خودی خود، هیچ میانبری به مهاجم در برابر تابع هش اصلی نمیدهد. بنابراین salt در پایگاه داده در کنار hash قرار دارد و این قرارگیری خوب است، حتی تشویق میشود. قانون سوم: مقادیر salt از یک مولد اعداد شبهتصادفی رمزنگاریشده امن بیرون میآیند. لینوکس یکی از آنها را از طریق `getrandom(2)` در معرض نمایش قرار میدهد. نود آن را `crypto.randomBytes` مینامد. کتابخانه استاندارد پایتون آن را در `secrets.token_bytes` قرار میدهد. `Math.random()` غیر رمزنگاریشده نامناسب است، حتی اگر در کد حسابرسیشده واقعی بسیار بیشتر از آنچه که باید، ظاهر شود.
دو نفر که "hunter2" را به عنوان رمز عبور خود انتخاب میکنند، باید با دو هش ذخیره شده کاملاً متفاوت از کار خود خارج شوند. Salting چیزی است که باعث این اتفاق میشود. اگر Salt را حذف کنید، پایگاه داده نه تنها هشها، بلکه نمودار اجتماعی اینکه چه کسی کدام رمز عبور را با چه کسی به اشتراک گذاشته است را نیز فاش میکند.
نمک زدن رمز عبور چگونه کار میکند؟
این مکانیزم به چهار مرحله تقسیم میشود و ده سال نقض امنیتی در ذخیرهسازی رمز عبور نشان میدهد که تقریباً هر شکستی به این دلیل رخ میدهد که کسی یکی از این مراحل را نادیده گرفته است.
مرحله اول در زمان ثبت نام اجرا میشود. برنامه از CSPRNG شانزده تا سی و دو بایت تصادفی درخواست میکند. این نمک است. راهنمای OWASP برای سال 2025، شانزده بایت (128 بیت) را به عنوان حداقل در نظر میگیرد؛ auth0 و چندین فروشنده مدیریت رمز عبور، سی و دو بیت را توصیه میکنند. SP 800-63B-4 NIST حداقل چهار بایت را تعیین میکند، اما هشدار میدهد که برخوردهای مربوط به تاریخ تولد، حدود شصت و پنج هزار کاربر را در این اندازه نشان میدهد، به همین دلیل است که هیچ کس در واقع از چهار بایت استفاده نمیکند.
مرحله دوم، salt را با رمز عبور انتخابی کاربر ترکیب میکند. الحاق رایجترین شکل است که در آن salt قبل یا بعد از رشته رمز عبور قرار میگیرد. برخی سیستمها به جای آن از HMAC استفاده میکنند و salt را به عنوان کلید HMAC در نظر میگیرند که از برخی مشکلات مبهم مربوط به افزایش طول در الحاق خام جلوگیری میکند.
مرحله سوم، ورودی ترکیبی را از طریق یک الگوریتم هشینگ رمز عبور اجرا میکند - چیزی که در کتابهای رمزنگاری، تابع استخراج کلید یا KDF نامیده میشود. این مرحلهای است که اکثر تیمها در آن شکست میخوردند، اغلب با رسیدن به یک الگوریتم هش عمومی و در نظر گرفتن کار انجام شده. SHA-256 ساده به عنوان راهی برای ذخیره رمزهای عبور به تنهایی نامناسب است. یک پردازنده گرافیکی مصرفکننده در سال 2026 بیش از صد میلیارد هش SHA-256 را در ثانیه طی میکند، بنابراین حتی با نمک، هزینه هر حدس در هشینگ رمزنگاری ناچیز باقی میماند. نمک زدن، بردارهای حمله جدول رنگینکمانی را از بین میبرد. به خودی خود، شکستن رمز عبور با نیروی بیرحمانه را کند نمیکند. ابزار مناسب در اینجا یک تابع عمداً کند و حافظهمحور است: Argon2id پیشفرض ترجیحی OWASP است، با scrypt و bcrypt قابل قبول، و PBKDF2 با تعداد تکرار بسیار بالا در جایی که انطباق با FIPS-140 انتخاب را ایجاب میکند، مجاز است.
مرحله چهارم، هم هش و هم سالت را ذخیره میکند. کتابخانههای مدرن فرمت ذخیرهسازی را مدیریت میکنند، بنابراین لازم نیست سالت ذخیره شده را به صورت دستی مدیریت کنید. خروجی bcrypt به شکل `$2b$12$9f4c8a7b...kQR8YZpL9` است - این رشته واحد شامل شناسه الگوریتم، عامل هزینه، سالت و مقدار هش است. Argon2id از فرمت رشته PHC مشابه، `$argon2id$v=19$m=19456,t=2,p=1$$`، استفاده میکند. شما آن رشته را در یک ستون پایگاه داده مینویسید و از آن خارج میشوید. کتابخانه هر کاری را که برای ذخیره ایمن رمزهای عبور لازم است انجام داده است - دادههای تصادفی برای سالت تولید کرده، رمز عبور را از طریق یک الگوریتم هش کند اجرا کرده و نتیجه را برای بازیابی بستهبندی کرده است.
ورود به سیستم (Login) از همین خط لوله به صورت معکوس پیروی میکند. برنامه، salt کاربر و hash ذخیره شده را جستجو میکند، رمز عبور ارسالی را از طریق همان الگوریتم با salt یکسان اجرا میکند و نتیجه را با hash ذخیره شده با استفاده از یک مقایسه زمان ثابت مقایسه میکند. زمان ثابت مهم است: یک `==` ساده، اطلاعاتی در مورد تعداد بایتهای منطبق فاش میکند، که باعث آسیبپذیریهای حمله زمان واقعی شده است. از `hmac.compare_digest`، `crypto.timingSafeEqual` یا معادل آن در چارچوب خود استفاده کنید.

چرا نمک زدن مهم است: مشکل میز رنگینکمانی
جداول رنگینکمانی، حملهی خاصی هستند که نمکپاشی برای شکست دادن آن اختراع شد. یک جدول جستجوی از پیش محاسبهشده را تصور کنید که هر رمز عبور محتمل - هر کلمهی دیکشنری، هر گونه تغییر رایج، هر ورودی لیست لو رفته - را به هش SHA-256 خود نگاشت میکند. این جداول وجود دارند، در انجمنهای کرک فروخته میشوند و حجم آنها به ترابایت میرسد. هنگامی که یک مهاجم یک کپی از هشهای بدون نمک در پایگاه داده داشته باشد، جستجوی یک هش در یک جدول رنگینکمانی یک پرسوجوی پایگاه داده است، نه یک کار کرک. لینکدین ۲۰۱۲ دقیقاً به همین شکل پیش رفت: ۱۱۷ میلیون هش SHA-1 بدون نمک در برابر یک جدول رنگینکمانی، که نود درصد آنها تقریباً در سه روز کرک شدند.
اگر یک salt به ازای هر کاربر اضافه کنید، جداول رنگینکمانی دیگر کار نمیکنند. مهاجم برای هر مقدار salt منحصر به فرد به یک جدول از پیش محاسبه شده جداگانه نیاز دارد. با یک salt تصادفی شانزده بایتی، این به معنای ۱۱۷ میلیون جدول مختلف برای ۱۱۷ میلیون کاربر است - و هر جدول همچنان ترابایت بزرگ خواهد بود. هزینههای ذخیرهسازی به تنهایی این را غیرممکن میکند. حمله از مدل تهدید خارج میشود.
این تمام کار Salt است. عمداً محدود است. Salting تلاش مصممانهی Brute-Force علیه یک کاربر خاص را کند نمیکند. Salting جلوی پر کردن اعتبارنامهها از نشت قبلی را نمیگیرد. Salting کمکی نمیکند اگر خود رمز عبور "123456" باشد - مهاجم فرهنگ لغت واضح را امتحان میکند و Salt برای هر حدس دوباره محاسبه میشود اما هزینه هر حدس را به طور معناداری تغییر نمیدهد.
آنچه Salting به طور قابل اعتمادی کاهش میدهد: جداول رنگینکمانی و نشت اطلاعات استفاده مجدد از رمز عبور بین کاربران در یک پایگاه داده. آنچه Hashing کند کاهش میدهد: هزینه هر حدس. آنچه Pepper کاهش میدهد: سرقت فقط پایگاه داده. آنچه MFA کاهش میدهد: پر کردن اعتبارنامه. Salting یک لایه امنیتی در یک پشته است - اضافه کردن Salt برای سختتر کردن شکستن رمزهای عبور با سختتر کردن حدس زدن هر رمز عبور به صورت جداگانه یکسان نیست. هدف Salting دستیابی به یک هش متفاوت برای هر کاربر است. پرهزینه کردن هر حدس یک مشکل جداگانه است.
هشینگ در مقابل رمزگذاری در مقابل نمک زدن
سه اصطلاحی که به خصوص در نوشتههای افراد غیرمتخصص با هم اشتباه گرفته میشوند. آنها قابل تعویض نیستند.
| رمزگذاری | هش کردن | نمک سود کردن | |
|---|---|---|---|
| برگشتپذیر | بله، با کلید | نه، طبق طراحی | اصلاحکننده، نه مستقل |
| طول خروجی | متغیر، با ورودی مطابقت دارد | ثابت (معمولاً ۲۵۶ بیت) | ناموجود — ورودی هش را تغییر میدهد |
| مورد استفاده برای | محرمانگی دادهها | یکپارچگی، ذخیره سازی رمز عبور | تقویت هشها |
| کلید مورد نیاز | بله، مخفیانه | خیر | خیر، از نمک تصادفی استفاده میکند |
رمزگذاری از دادههایی که میخواهید بعداً بازیابی کنید محافظت میکند. گیرنده کلید را نگه میدارد، آن را اعمال میکند و نسخه اصلی را میخواند. هشینگ یک طرفه است: وقتی یک رمز عبور به هش تبدیل میشود، هیچ الگوریتمی آن را معکوس نمیکند. نمک زدن یک اصلاحکننده است که شما به ورودی یک تابع هش اعمال میکنید - به خودی خود هیچ معنایی ندارد.
نقض امنیتی ادوبی در سال ۲۰۱۳، نمونهی هشداردهندهای در اینجا است. ادوبی با رمزگذاری رمزهای عبور با ۳DES در حالت ECB و تحت یک کلید واحد در کل سایت، ۱۵۳ میلیون رکورد کاربر را ذخیره کرد. این انتخاب یک شکست سهگانه بود. رمزگذاری، روش اولیهی نادرستی برای ذخیرهسازی رمز عبور است. حالت ECB ورودیهای یکسان را به عنوان خروجیهای یکسان فاش میکند. استفادهی مجدد از یک کلید در کل پایگاه داده به این معنی بود که رمزگشایی کلید یک بار، تمام ۱۵۳ میلیون رکورد را رمزگشایی میکرد. اشنایر آن را یکی از بدترین نقضهای رمز عبور در تاریخ نامید. راهحل در هر لایه، تغییر به هشینگ نمکی و کند بود.
نمک در مقابل فلفل: دفاع دو لایه
پپر (Pepper) پسرعموی کمتر شناختهشدهی سالت (salt) است. مفهوم: یک مقدار مخفی اضافی که به هر رمز عبوری که برنامه پردازش میکند اعمال میشود، اما خارج از پایگاه داده ذخیره میشود. یک متغیر محیطی، یک ورودی سرویس مدیریت کلید یا یک کلید مقیم HSM. سالت (salt) در کنار هش به صورت متن ساده قرار میگیرد. پپر اینطور نیست.
مدل تهدید، سرقت فقط پایگاه داده است. اگر یک مهاجم فقط پایگاه داده را سرقت کند - از طریق تزریق SQL، یک نسخه پشتیبان پیکربندی نشده یا یک نسخه پشتیبان نشت یافته - آنها salts و hash ها را دارند اما pepper را ندارند. بدون pepper، آنها حتی نمیتوانند brute-forcing را شروع کنند زیرا هر حدسی که آنها hash میکنند، اصلاح کننده secret سراسری را از دست میدهد.
یک پیادهسازی معمول، ابتدا `argon2id(salt + password + pepper)` را اجرا میکند یا با دقت بیشتر، `HMAC(pepper, password + salt)` را محاسبه کرده و نتیجه را به Argon2id میدهد. OWASP، pepper را برای سیستمهای با ارزش بالا توصیه میکند، اما این بدهبستان را نیز میپذیرد: چرخش pepper از نظر عملیاتی دشوار است. چرخش آن به معنای هش کردن مجدد رمز عبور هر کاربر در ورود بعدی است و اگر pepper بدون پشتیبانگیری گم شود، هر حساب غیرقابل بررسی میشود. اکثر برنامههای مصرفی pepper را نادیده میگیرند. بانکها، دولتها و بارهای کاری مراقبتهای بهداشتی معمولاً این کار را نمیکنند.
هشینگ رمز عبور مدرن در سال ۲۰۲۶
راهنمای ذخیرهسازی رمز عبور OWASP برای سال ۲۰۲۵، چهار الگوریتم را به ترتیب اولویت رتبهبندی میکند. Salt در همه آنها تعبیه شده است - شما آن را به صورت دستی تولید نمیکنید.
| الگوریتم | حداقل الزامات OWASP 2025 | مشخصات |
|---|---|---|
| Argon2id (ترجیحاً) | m=19 مگابایت، t=2، p=1 | RFC 9106 (2021) |
| اسکریپت | N=2^17، r=8، p=1 | RFC 7914 (2016) |
| bcrypt (قدیمی) | هزینه ≥ ۱۰؛ ظرفیت ورودی ۷۲ بایت | نیلز پرووس، ۱۹۹۹ |
| PBKDF2-HMAC-SH256 | ۶۰۰۰۰۰ تکرار | RFC 2898؛ فقط FIPS |
Argon2id حافظهمحور است، به این معنی که هر حدس نه تنها چرخههای CPU، بلکه بخش مشخصی از RAM را نیز مصرف میکند. حداقل ۱۹ مگابایت در هر هش طبق OWASP به این معنی است که مهاجمی که یک ASIC سفارشی میسازد، باید حافظه سریع زیادی نیز بسازد و حافظه بخش گرانقیمت هر ریگی است. نوع "id" با اجرای Argon2i برای نیمه اول و Argon2d برای نیمه دوم، Argon2i (مقاوم در برابر کانال جانبی) و Argon2d (مقاوم در برابر GPU) را ترکیب میکند.
scrypt از Argon2id قدیمیتر است و بر اساس همان اصل حافظه-سخت کار میکند. bcrypt از این هم قدیمیتر، سادهتر است و محدودیت ورودی ۷۲ بایتی دارد که گاهی اوقات برنامهها را با استفاده از عبارات عبور طولانی دچار خطا میکند - اگر به ورودیهای طولانیتر نیاز دارید، با SHA-256 و base64-encode از قبل هش کنید. PBKDF2 انتخاب کند اما سازگار با FIPS است. OWASP تعداد تکرار خود را برای SHA-256 در سال ۲۰۲۳ به ۶۰۰۰۰۰ افزایش داد، که از ۳۱۰۰۰۰ افزایش یافته است.
چه چیزهایی در این لیست جایی ندارند: SHA-256 ساده، SHA-512 ساده، MD5، SHA-1 و هر تابع سفارشی که به "+ salt" ختم میشود. سرعت، عامل رد صلاحیت است. SHA-256 برای سرعت بالا در سختافزارهای معمولی طراحی شده است. این دقیقاً برعکس چیزی است که ذخیرهسازی رمز عبور به آن نیاز دارد.
اشتباهات رایج نمک زدن که در ممیزیهای واقعی ظاهر میشوند
گزارشهای نقض امنیتی در دنیای واقعی، همچنان همان تعداد انگشتشمار خطا را نشان میدهند.
استفاده مجدد از یک salt برای هر کاربر، کل مکانیسم را مختل میکند - مشکل جدول رنگینکمان دوباره برمیگردد، فقط با یک مرحله اضافی. استفاده از نام کاربری به عنوان salt بدتر است، زیرا نامهای کاربری در سیستمها تکرار میشوند و جداول رنگینکمان را میتوان برای جداول محبوب از پیش محاسبه کرد. saltهایی با طول کمتر از شانزده بایت، در مقیاس پایگاههای کاربری بزرگ، شروع به پذیرش تصادم میکنند. تولید salt با یک تابع تصادفی غیر رمزنگاری - `Math.random()`، `time(0)` mod something، RNG پیشفرض زبان - مقادیر قابل پیشبینی تولید میکند که salting را از مزایای آن محروم میکند.
یک اشتباه زیرکانهتر، ذخیره Salt روی یک سرور دیگر «برای امنیت» است. Salt یک راز نیست. تقسیم آن بین سیستمها، پیچیدگی عملیاتی را بدون افزایش هیچ قدرت رمزنگاری اضافه میکند. آن را در همان ردیفی که Hash قرار دارد ذخیره کنید. رشتههای PHC مدرن این کار را برای شما انجام میدهند.
بزرگترین مورد، در حسابرسیهای سال ۲۰۲۶، هش کردن با SHA-256 ساده به همراه salt و shipping است. salt از جداول رنگینکمانی جلوگیری میکند. این salt هیچ کاری در مورد یک مزرعه GPU که ده میلیارد حدس در ثانیه را علیه یک حساب کاربری اجرا میکند، انجام نمیدهد. راه حل اضافه کردن salt دوم نیست؛ بلکه تغییر به یک KDF کند مانند Argon2id است.
آخرین مورد: هشهای قدیمی بدون نمک. مهاجرت اختیاری نیست. الگوی استاندارد این است که هش قدیمی در ورود بعدی تأیید شود، با الگوریتم مدرن دوباره هش شود و بازنویسی شود. برای کاربرانی که دیگر هرگز وارد سیستم نمیشوند، تنظیم مجدد رمز عبور در یک جدول زمانی ثابت اجباری شود.

چرا نمک زدن به تنهایی کافی نیست
Salting یک لایه است، نه یک دژ. این روش یک حمله خاص - جداول از پیش محاسبه شده - را شکست میدهد و چیزی در مورد قدرت رمز عبور اصلی فاش نمیکند. حمله Brute Force هنوز در برابر رمزهای عبور ضعیف کار میکند. Credential stuffing هنوز در برابر رمزهای عبور استفاده شده مجدد کار میکند. فیشینگ هنوز در برابر هر رمز عبوری کار میکند.
امنیت واقعی رمز عبور در سال ۲۰۲۶، چهار راهکار دفاعی را در کنار هم قرار میدهد. Argon2id با یک salt شانزده بایتی برای هر کاربر، فضای ذخیرهسازی را مدیریت میکند. pepper، اختیاری اما ارزشمند برای سیستمهای حساس، سرقت فقط از پایگاه داده را مسدود میکند. احراز هویت چند عاملی، پر کردن اعتبارنامه و بیشتر فیشینگها را مسدود میکند. نظارت مداوم بر نقض امنیتی - سرویسهایی مانند Have I Been Pwned و API آن - لحظهای را که اعتبارنامههای کاربر در یک نشت ظاهر میشوند، ثبت میکند تا بتوان آنها را مجبور به تنظیم مجدد کرد.
اگر از هر یک از این لایهها بگذرید، مهاجم در نهایت شکاف را پیدا میکند. Salting به تنهایی، یا هر دفاع به تنهایی، دقیقاً همان چیزی است که هر نقض امنیتی پس از بررسی در دهه گذشته به عنوان نقطه شکست شناسایی کرده است.