تابع crypt() در PHP
تابع crypt() در PHP یک رابط قدیمی و انعطافپذیر برای تولید هش رمزنگاریشده با استفاده از الگوریتمها و فرمتهای مختلف است. این تابع در کاربردهایی مثل ذخیرهسازی رمز عبور، بررسی اعتبار و مهاجرت بین الگوریتمها کاربرد دارد. در این مقاله به صورت دقیق، امن و عملی دربارهٔ crypt()، فرمت «salt»، انواع الگوریتمها، مثالهای واقعی و نکات امنیتی خواهیم پرداخت.
چرا crypt() و چه محدودیتهایی دارد؟
- crypt() میتواند الگوریتمهای مختلف (DES، MD5، Blowfish، SHA-256، SHA-512 و…) را با استفاده از فرمتهای مشخص اجرا کند.
- اما پیادهسازی و انتخاب salt بر عهدهٔ توسعهدهنده است؛ اشتباه در ساخت salt میتواند امنیت را کاهش دهد.
- برای رمزنگاری رمز عبور، امروزه توصیهٔ اصلی استفاده از password_hash() و password_verify() است؛ این توابع بهصورت خودکار salt را مدیریت و پارامترهای الگوریتم را بهینه میکنند.
نحو کلی استفاده
تابع crypt دو پارامتر میگیرد: مقدار ورودی (مثلاً رمز عبور) و salt (یا رشتهٔ هش پیشفرمتشده که حاوی شناسهٔ الگوریتم است).
$hash = crypt($password, $salt);در این مثال، $salt تعیین میکند که از چه الگوریتم و چه پارامتری استفاده شود. اگر salt رشتهٔ هش تولید شده قبلی باشد، crypt میتواند خروجیای سازگار تولید کند و برای تأیید رمز از آن استفاده شود.
فرمت و پیشوندهای رایج salt
| پیشوند | الگوریتم | توضیح |
|---|---|---|
| $1$ | MD5 | نسخهٔ MD5-based، نه بسیار امن برای رمز عبور |
| $2y$ | Blowfish (bcrypt) | bcrypt — مناسب برای ذخیرهٔ رمز عبور در PHP |
| $5$ | SHA-256 | SHA-256 با امکان تعیین تعداد تکرار (rounds) |
| $6$ | SHA-512 | قویتر از SHA-256، با امکان تعیین rounds |
نمونهٔ کاربردی: تولید هش با SHA-512 و بررسی آن
// تولید salt با فرمت SHA-512
$salt = sprintf('$6$rounds=%d$%s$', 5000, substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 16));
$password = 'MyP@ssw0rd!';
$hash = crypt($password, $salt);
// ذخیره $hash در دیتابیس و برای بررسی:
$isValid = hash_equals($hash, crypt($password, $hash));توضیح: در این کد ابتدا یک salt برای SHA-512 درست میکنیم که شامل پارامتر rounds (تعداد تکرار) است. سپس crypt هش را تولید میکند. برای تأیید، دوباره crypt را با salt برابر رشتهٔ هش قبلی فراخوانی میکنیم و با hash_equals مقایسه مینماییم تا از حملات زمانبندی (timing attack) جلوگیری شود.
نمونهٔ ساده با bcrypt (Blowfish)
// اگر CRYPT_BLOWFISH فعال باشد
$salt = sprintf('$2y$%02d$%s', 10, substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22));
$hash = crypt('MySecret', $salt);
// بررسی
if (hash_equals($hash, crypt('MySecret', $hash))) {
// رمز صحیح است
}توضیح: bcrypt با پیشوند $2y$ و پارامتر cost (مثلاً 10) استفاده میشود. اندازه salt برای bcrypt باید دقیقاً 22 کاراکتر base64-like باشد. استفاده از random_bytes بهجای توابع ضعیفتر (مثل rand یا mt_rand) برای تولید salt ضروری است.
بررسی قابلیتها و سازگاری
- در PHP با چک کردن ثابتهای CRYPT_* میتوانید ببینید کدام الگوریتمها در سیستم قابلدسترسیاند، مثال: CRYPT_BLOWFISH، CRYPT_SHA512.
- در محیطهایی که bcrypt در دسترس نیست، fallback مناسب نیاز است (اما بهتر است از password_hash استفاده کنید که پرتپرتوافزاری را مدیریت میکند).
چرا هنوز بعضیها از crypt() استفاده میکنند؟
crypt() انعطافپذیری بالایی دارد و در پروژههای قدیمی به کار رفته است؛ همچنین برای پیادهسازیهای پیشرفته که نیاز به کنترل دقیق پارامترها دارند مفید است. اما توسعهدهندگان حرفهای معمولاً از password_hash و password_verify برای رمزهای عبور استفاده میکنند چون امنیت و سادگی بالاتری فراهم میکنند.
مقایسه سریع با password_hash
- password_hash: مدیریت خودکار salt، انتخاب الگوریتم قوی (bcrypt، Argon2 در نسخههای جدید)، API ساده و امن.
- crypt: کنترل دقیق بر فرمت salt و پارامترها، مناسب برای سازگاری با سیستمهای قدیمی یا الگوریتمهای خاص.
نکات امنیتی و بهترین شیوهها
- هرگز از توابع تصادفی ضعیف برای تولید salt استفاده نکنید؛ از random_bytes یا openssl_random_pseudo_bytes استفاده کنید.
- همیشه از hash_equals برای مقایسهٔ هشها استفاده کنید تا در برابر حملات زمانبندی امن باشید.
- برای رمزهای عبور جدید از password_hash (و در صورت امکان Argon2) استفاده کنید؛ crypt فقط در موارد خاص لازم است.
- حداقل cost یا rounds را متناسب با توان سرور تعیین کنید؛ هزینهٔ محاسباتی بیش از حد باعث افت کارایی میشود و کم بودن آن امنیت را کاهش میدهد.
- از الگوریتمهای ضعیف مانند MD5 یا DES برای رمز عبور استفاده نکنید.
مثال کامل: تابع کمکی بررسی و مهاجرت از crypt به password_hash
function verifyPassword($password, $storedHash) {
// اگر hash با password_hash تولید شده باشد، از password_verify استفاده شود
if (strpos($storedHash, '$2y$') === 0 || strpos($storedHash, '$argon2') === 0 || strpos($storedHash, '$5$') === false) {
// fallback: سعی میکنیم با crypt بررسی کنیم
return hash_equals($storedHash, crypt($password, $storedHash));
}
// در حالت کلی از password_verify استفاده کن
return password_verify($password, $storedHash);
}توضیح: این تابع مثالی از نحوهٔ بررسی است که سعی میکند حالات مختلف را پوشش دهد. برای رمزهای جدید ترجیحاً password_verify و برای مقادیر قدیمی که با crypt ساخته شدهاند از crypt + hash_equals استفاده کنید. در عمل باید قیاسهای دقیقتری براساس فرمت ذخیرهشده انجام شود.
جمعبندی
تابع crypt() ابزار قدرتمندی برای تولید هش است، اما خطراتی هم در صورت استفادهٔ ناصحیح دارد. برای پروژههای جدید بهطور عمومی از password_hash و password_verify استفاده کنید و در موارد خاص که نیاز به کنترل دقیق پارامترها یا سازگاری با سیستمهای قدیمی دارید، crypt همراه با salt مناسب و مقایسهٔ امن (hash_equals) قابلاطمینان است. همواره از توابع تولید تصادفی امن و الگوریتمهای مقاوم در برابر حملات روز استفاده کنید.
آیا این مطلب برای شما مفید بود ؟



