ویژگی تصویر

کتابخانه bcrypt در پایتون — معرفی جامع و بهترین روش‌ها

  /  پایتون   /  کتابخانه bcrypt در پایتون
بنر تبلیغاتی الف

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

چرا bcrypt؟

  • قابلیت تنظیم هزینه (work factor): با افزایش rounds هزینه محاسباتی افزایش می‌یابد و حملات سخت‌افزاری را گران‌تر می‌کند.
  • Salt خودکار: هر هش دارای salt اختصاصی است، که جدول‌های ازپیش‌محاسبه‌شده (rainbow tables) را بی‌اثر می‌کند.
  • مقاومت در برابر حملات زمان‌بندی: توابعی مثل checkpw برای جلوگیری از تفاوت‌های زمانی طراحی شده‌اند.

نصب و آماده‌سازی

نصب معمولاً با pip انجام می‌شود:

pip install bcrypt

این دستور بسته باینری bcrypt را نصب می‌کند که روی CPython سریع و قابل اعتماد است.

توابع اصلی کتابخانه

تابعکارکرد
gensalt(rounds=12)ایجاد salt و تعیین هزینه (rounds یا log_rounds)
hashpw(password, salt)تولید هش از password (هر دو باید bytes باشند)
checkpw(password, hashed)بررسی مطابقت password با hashed؛ اجرای زمان‌ثابت

مثال ساده: هش کردن و اعتبارسنجی

import bcrypt

# هش کردن
password = b"my_secret_password"
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)
print(hashed)

# اعتبارسنجی
if bcrypt.checkpw(password, hashed):
    print("Password matches")
else:
    print("Invalid password")

در این کد، ابتدا password را به صورت بایت ارائه می‌کنیم. bcrypt.gensalt یک salt با پارامتر rounds تولید می‌کند. bcrypt.hashpw با ترکیب salt و password، هش تولید می‌کند. bcrypt.checkpw به‌صورت امن تطابق را بررسی می‌کند.

اشتباه رایج: ارسال رشته به‌جای bytes

import bcrypt

password = "password123"           # رشته (str) — اشتباه
salt = bcrypt.gensalt()
# bcrypt.hashpw(password, salt)    # خطا: password باید bytes باشد

کتابخانه bcrypt انتظار بایت‌ها را دارد. برای رفع مشکل، رشته را با encode تبدیل کنید:

password = "password123".encode("utf-8")
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password, salt)

استفاده از UTF-8 توصیه می‌شود تا از مشکلات کاراکترهای یونی‌کد جلوگیری شود.

نکات پیشرفته و بهترین روش‌ها

  • انتخاب rounds مناسب: مقدار پیش‌فرض معمولاً 12 است؛ اما باید بر اساس تست زمانی روی سرور شما تنظیم شود. مقدار بیشتر امنیت را بالا می‌برد اما مصرف CPU را نیز افزایش می‌دهد.
  • ذخیره‌سازی: hashed تولیدشده شامل salt و پارامتر rounds است، بنابراین کافی است رشته هش را (bytes یا base64/utf-8) در دیتابیس ذخیره کنید.
  • مهاجرت هزینه (re-hashing): هنگام افزایش rounds می‌توانید در زمان ورود کاربر، پس از verify، مجدداً با rounds جدید هش کنید و ذخیره کنید.
  • حملات زمان‌بندی: از checkpw استفاده کنید که طراحی شده تا زمان ثابت داشته باشد. از مقایسه‌های ساده رشته‌ای خودداری کنید.
  • محدودیت طول رمز: bcrypt رشته‌های طولانی‌تر از ~72 بایت را برش می‌دهد؛ در صورت نیاز به پشتیبانی از رمزهای طولانی، از KDFهای دیگر یا ابتدا اجرای KDF مثل scrypt/argon2 یا SHA-256 سپس bcrypt روی خروجی استفاده کنید (با توجه به پیامدهای امنیتی).

مثال مهاجرت rounds (روی ورود کاربر)

import bcrypt

CURRENT_ROUNDS = 14

def verify_and_migrate(password_plaintext, stored_hash):
    password = password_plaintext.encode("utf-8")
    if bcrypt.checkpw(password, stored_hash):
        # بررسی اینکه آیا rounds پایین‌تر است
        # stored_hash به صورت bytes است و رشته‌ای شبیه b"$2b$12$..." دارد
        parts = stored_hash.split(b"$")
        rounds = int(parts[2]) if len(parts) > 2 else None
        if rounds and rounds < CURRENT_ROUNDS:
            new_salt = bcrypt.gensalt(rounds=CURRENT_ROUNDS)
            new_hash = bcrypt.hashpw(password, new_salt)
            # این new_hash را در DB به‌روزرسانی کنید
            return True, new_hash
        return True, None
    return False, None

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

مقایسه اجمالی: bcrypt vs Argon2 vs scrypt

  • bcrypt: پراستفاده، امن، اما محدود به 72 بایت ورودی و کمتر مقاوم به حملات مبتنی بر حافظه نسبت به Argon2.
  • Argon2: برنده از نظر مقاومت حافظه، مدرن‌تر و توصیه‌شده برای برنامه‌های جدید؛ پیاده‌سازی در پایتون با libargon2 موجود است.
  • scrypt: طرحی با مصرف حافظه قابل تنظیم که گزینه‌ای میانی است.

همکاری با فریم‌ورک‌ها (Django/Flask)

در Django نسخه‌های جدید، سیستم رمزنگاری پیش‌فرض از PBKDF2 است ولی می‌توانید با استفاده از کتابخانه‌هایی مثل django-bcrypt یا passlib، bcrypt را به‌عنوان الگوریتم رمزنگاری فعال کنید. در Flask معمولاً خودتان با bcrypt.hashpw/ checkpw کار می‌کنید یا از Flask-Bcrypt بهره می‌برید.

نکات عملیاتی و امنیتی

  • هش‌ها را با مجوزهای مناسب و در فیلدهای امن دیتابیس ذخیره کنید (مثلاً ستون varbinary یا text بدون لو رفتن لاگ‌ها).
  • هرگز salt یا هش را در لاگ‌ها ننویسید.
  • بسته به قدرت سرور، مقدار rounds را سالانه بازبینی کنید و در صورت نیاز افزایش دهید.
  • برای تست عملکرد، زمان اجرای hashpw را اندازه‌گیری کنید و تنظیم rounds را طوری انجام دهید که عملیات ورود کاربران قابل قبول باشد (مثلاً 100-500 میلی‌ثانیه).

ابزار کوچک برای تست زمان هش

import time
import bcrypt

def measure_time(rounds, trials=3):
    password = b"benchmark_password"
    start = time.time()
    for _ in range(trials):
        salt = bcrypt.gensalt(rounds=rounds)
        bcrypt.hashpw(password, salt)
    end = time.time()
    print(f"Rounds: {rounds}, avg: {(end-start)/trials:.3f}s")

for r in [10, 12, 14]:
    measure_time(r)

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

خلاصه و جمع‌بندی

bcrypt کتابخانه‌ای بالغ و امن برای مدیریت گذرواژه‌ها است. با توجه به محدودیت‌های طول ورودی و پیشرفت الگوریتم‌های جدیدتر مثل Argon2، توصیه می‌شود برای پروژه‌های جدید مقایسه‌ای میان الگوریتم‌ها انجام دهید، اما bcrypt همچنان انتخاب قابل اعتمادی است. رعایت نکات مربوط به تبدیل به bytes، تنظیم rounds مناسب، مهاجرت تدریجی هش‌ها و جلوگیری از قرار گرفتن هش‌ها در لاگ‌ها از ملزومات پیاده‌سازی امن است.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: