رفتن به محتوای اصلی

امنیت و برنامه‌نویسی

هش، رمزگذاری و رمز عبور؛ چرا SHA-256 برای پسورد کافی نیست؟

تفاوت encoding، hashing، encryption و امضای دیجیتال را با مثال بفهم. آزمایش SHA-256 در مرورگر و راه درست سؤال پرسیدن از AI دربارهٔ امنیت.

هش، رمزگذاری و رمز عبور؛ چرا SHA-256 برای پسورد کافی نیست؟

تصویر اختصاصی مجلهٔ ساروی؛ ساخته‌شده با هوش مصنوعی

دو رشتهٔ تقریباً یکسان را در آزمایش بالای این درس وارد کن. خروجی‌های SHA-256 متفاوت دیده می‌شوند. این تفاوت به معنی رمزگذاری متن نیست: کلیدی نداریم که هش را به متن اولیه برگرداند. همین تمایز، جلوی اشتباه رایج پروژه‌های دانشجویی را می‌گیرد؛ ذخیرهٔ پسورد با یک دور SHA-256.

این کار را در ساروی شروع کن
آزمایش داخل مرورگر

فقط یک حرف را عوض کن.

متن معمولی بنویس و اثر آن را روی SHA-256 ببین. این آزمایش رمز عبور ذخیره نمی‌کند؛ ورودی‌ها در همین مرورگر محاسبه می‌شوند.

۱. چهار مسئلهٔ متفاوت

فرض کن فایلی را برای دوستت می‌فرستی. شاید می‌خواهی محتوای آن محرمانه بماند؛ شاید فقط می‌خواهی بفهمد فایل تغییر نکرده؛ شاید می‌خواهی ثابت کنی تو آن را تأیید کرده‌ای. این سه نیاز، راه‌حل یکسان ندارند.

Base64 نمایش داده را عوض می‌کند تا در مسیر متنی جابه‌جا شود؛ هیچ راز یا کلیدی به آن اضافه نمی‌کند. کسی که رشتهٔ Base64 را دارد معمولاً می‌تواند داده را decode کند.

ابزارپرسش اصلیکلید یا راز
Encoding مثل Base64داده را در چه قالبی نمایش دهم؟ندارد
هش رمزنگارانهاثر انگشت این داده چیست؟در هش ساده ندارد
رمزگذاریچطور فقط گیرندهٔ مجاز متن را بخواند؟دارد
امضای دیجیتالآیا دارندهٔ کلید خصوصی این داده را امضا کرده؟کلید خصوصی و عمومی

۲. هش چه تضمینی می‌دهد؟

SHA-256 برای هر ورودی یک خروجی ۲۵۶بیتی می‌سازد که معمولاً با ۶۴ رقم hexadecimal نمایش داده می‌شود. ورودی یکسان، هش یکسان می‌دهد؛ حتی یک فاصلهٔ اضافه ورودی را عوض می‌کند. مستندات digest روش اجرای آن را در مرورگر توضیح می‌دهد.

خروجی ثابت‌طول یعنی برخورد از نظر ریاضی وجود دارد: تعداد ورودی‌ها نامحدود است اما تعداد خروجی‌ها محدود. خواستهٔ امنیتی این است که پیدا کردن برخورد یا ورودی مناسب از نظر محاسباتی عملی نباشد. پس «هیچ دو متنی هش یکسان ندارند» جملهٔ درستی نیست.

۳. آزمایش را در مرورگر تکرار کن

TextEncoder متن را به بایت UTF-8 تبدیل می‌کند. digest نتیجه را به صورت ArrayBuffer می‌دهد. هر بایت را به دو رقم hexadecimal تبدیل و به هم وصل می‌کنیم. Web Crypto در محیط امن مانند HTTPS در دسترس است.

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

sha256.js javascript
async function sha256(text) {
  const bytes = new TextEncoder().encode(text);
  const buffer = await crypto.subtle.digest("SHA-256", bytes);
  return Array.from(new Uint8Array(buffer))
    .map(byte => byte.toString(16).padStart(2, "0"))
    .join("");
}
console.log(await sha256("Sarovi"));

۴. هش فایل به تنهایی اصالت را ثابت نمی‌کند

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

برای پیام بین دو طرفی که راز مشترک دارند، HMAC مسئلهٔ متفاوتی را حل می‌کند. برای امضای عمومی، جفت کلید خصوصی و عمومی مطرح می‌شود. انتخاب به این بستگی دارد که چه کسی باید بتواند تأیید کند و چه کسی اجازهٔ تولید تأیید را داشته باشد.

۵. مهاجم لازم نیست هش را «برگرداند»

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

SHA-256 برای بسیاری از کاربردها سریع است. ذخیرهٔ پسورد به تابعی نیاز دارد که هزینهٔ هر حدس را با پارامتر مناسب بالا ببرد. OWASP برای طراحی جدید Argon2id را پیشنهاد می‌کند. از پیاده‌سازی معتبر استفاده کن؛ یک حلقهٔ دستی روی SHA-256 جایگزین سامانهٔ ذخیرهٔ پسورد نیست.

۶. salt راز مشترک همهٔ کاربران نیست

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

salt پسورد ضعیف را قوی نمی‌کند. در برنامهٔ واقعی، کتابخانهٔ ذخیرهٔ پسورد تولید salt، قالب ذخیره و verify را مدیریت می‌کند. کلیدهای رمزگذاری، session، reset token و محدود کردن تلاش ورود هم مسائل جداگانه‌اند.

۷. رمزگذاری و امضا

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

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

۸. تمرین با ساروی

سه سناریو بنویس: بررسی تغییر فایل دانلودی، مخفی کردن نسخهٔ پشتیبان و ورود کاربر به سایت. برای هرکدام بگو کدام خاصیت لازم است: محرمانگی، تمامیت، اصالت یا verify پسورد. اگر همه را با «هش می‌کنم» جواب داده‌ای، به جدول اول برگرد.

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

حالا نوبت توست

این کار را خودت انجام بده.

  • هش دو متن با یک حرف متفاوت را مقایسه کن.
  • برای سه سناریو خاصیت امنیتی لازم را نام ببر.
  • ذخیرهٔ پسورد را از checksum فایل جدا طراحی کن.

برای بررسی بیشتر

MDN: SubtleCrypto.digestOWASP: Password Storage

در انجام این کار به مشکل خوردی؟

از پشتیبانی کمک بگیر