bcrypt مقابل Argon2 مقابل scrypt: تجزئة كلمات المرور 2026
الإجابة المختصرة: لأي مشروع جديد في 2026، استخدم Argon2id بمعاملات <bdi>m=19456, t=2, p=1</bdi>. هذا خط الأساس في OWASP Password Storage Cheat Sheet، وهو أقوى خوارزمية تجزئة كلمات مرور تستطيع نشرها اليوم في وجه هجمات GPU والقنوات الجانبية.
إن لم يكن Argon2 متاحًا في بيئتك (نادر، لكنه يحدث في بعض الأنظمة المُضمَّنة أو بيئات تشغيل قديمة)، اختر scrypt بمعاملات <bdi>N=2^17, r=8, p=1</bdi>. استخدم bcrypt بـ <bdi>cost=12</bdi> فقط حين تكون عالقًا في نظام قديم يعتمد bcrypt أصلًا ولا يمكنك إضافة اعتمادية جديدة. التزم بـ PBKDF2-HMAC-SHA-256 بـ 600,000 تكرار عند اشتراط التوافق مع FIPS-140.
| الخوارزمية | معاملات OWASP 2026 | متى تختارها |
|---|---|---|
| Argon2id | <bdi>m=19456 KiB, t=2, p=1</bdi> | الافتراضي للمشاريع الجديدة |
| scrypt | <bdi>N=2^17, r=8, p=1</bdi> | Argon2 غير متوفر |
| bcrypt | <bdi>cost=12</bdi> (الحد الأدنى 10) | الأنظمة القديمة فقط |
| PBKDF2 | HMAC-SHA-256، 600k تكرار | عند اشتراط FIPS-140 |
تشرح بقية المقالة سبب اختيار هذه الأرقام، وكيف تضبطها لعتادك، وكيف تُهاجر دون إجبار المستخدمين على إعادة تعيين كلمات المرور. تحتاج إلى توليد كلمات مرور اختبارية قوية لقياس الأداء؟ استخدم مولّد كلمات مرور عشوائية. للسياق الأوسع، راجع دليل أساسيات أمان الويب.
لماذا تختلف تجزئة كلمات المرور عن التجزئة العامة؟
دوال التجزئة تبدو متشابهة من الخارج: تدخل البيانات، تخرج بصمة بطول ثابت، ولا يمكنك عكسها. لكن أهداف تصميم “تجزئة هذا الـ ISO بحجم 4 GB” و”تجزئة كلمة مرور من 12 حرفًا” متعاكسة. الأولى يجب أن تعمل بسرعة السيليكون. والثانية يجب أن تكون بطيئة بقدر ما تحتمله ميزانيتك لزمن استجابة تسجيل الدخول.
الخلط بينهما هو ما يحوّل تسريبات قواعد البيانات إلى استيلاء على الحسابات.
لماذا لا يكفي MD5 و SHA-256 لكلمات المرور؟
التجزئات العامة مثل MD5 و SHA-1 و SHA-256 صُمِّمت من أجل الإنتاجية. تعالج جيجابايتات في الثانية على معالجات استهلاكية وعشرات الجيجابايتات في الثانية على GPU. هذا يجعلها ممتازة لمجاميع التحقق وعنونة المحتوى، وكارثية لكلمات المرور.
اختبارات Hashcat على بطاقة RTX 4090 وحيدة تُظهر نحو 164 GH/s لـ MD5 و**22 GH/s لـ SHA-256** في 2024. كلمة مرور من 8 أحرف صغيرة وأرقام (36^8 ≈ 2.8 × 10^12 احتمال) تسقط أمام GPU واحدة في أقل من دقيقة ضد MD5، وفي أقل من دقيقتين ضد SHA-256. قاعدة بيانات مسرّبة تخزّن <bdi>sha256(password)</bdi> هي عمليًا نص صريح.
والملح (salt) لن ينقذك. الملح يمنع جداول قوس قزح المُسبَقة من العمل، لكنه لا يفعل شيئًا لإبطاء هجوم على حساب بعينه: المهاجم ببساطة يجزّئ كل احتمال مع الملح المسرَّب.
أما لمجاميع التحقق غير الأمنية، فلا يزال MD5 و SHA-256 مفيدَين، وهذا ما صُمِّمت لأجله أدوات مثل مولّد تجزئة MD5. لمقارنة معمّقة لمتى يصلح كل خوارزمية، اقرأ MD5 مقابل SHA-256. أما كلمات المرور، فتحتاج إلى تجزئة بطيئة عمدًا.
الخصائص الثلاث لتجزئة كلمات المرور الحديثة
تجزئة كلمات المرور الجديرة بالنشر في 2026 تمتلك ثلاث خصائص:
- بطيئة بالتصميم، مع عامل عمل قابل للضبط. تسجيل الدخول يجب أن يستغرق 100 إلى 500 ms؛ سريع بما يكفي لئلا يلاحظه المستخدم، وبطيء بما يكفي ليحرق المهاجم خارج الخط أيامًا لكل مليون تخمين. عامل العمل لا بد أن يكون معاملًا قابلًا للضبط ترفعه مع تطور العتاد.
- ملح لكل سجل. ملح عشوائي فريد لكل كلمة مرور يهزم جداول قوس قزح ويُجبر المهاجم على مهاجمة كل حساب على حدة. الخوارزميات الحديثة تولّد الملح وتُضمِّنه في سلسلة المخرجات بدلًا منك.
- صعبة الذاكرة (memory-hard). GPU و ASIC سريعة في الحساب لكنها مكلفة في ذاكرة عالية الترددات. خوارزمية تتطلب عشرات الـ MiB لكل تجزئة تُجبر المهاجم على توفير ذاكرة RAM متناسبة مع التوازي، مما يقتل اقتصاديات مزارع GPU.
bcrypt يحقّق (1) و (2) دون (3). scrypt كان أول خوارزمية تحقّق الثلاث. Argon2 صقل التصميم وفاز بمسابقة تجزئة كلمات المرور. القسم التالي يفكّك كلًا منها بالتفصيل.
الخوارزميات الثلاث: البنية والمقايضات
bcrypt: مبنية على Blowfish وصعبة زمنيًا
صُمِّم bcrypt سنة 1999 من قِبل Niels Provos و David Mazières لـ OpenBSD. يُبنى على شيفرة Blowfish، مع طور تهيئة مفتاح مكلف (“EksBlowfish”) يُكرَّر 2^cost مرة. المعامل القابل للضبط الوحيد هو عامل التكلفة (يُسمى أيضًا “log rounds”): كل زيادة بمقدار واحد تضاعف العمل. تجزئة بـ <bdi>cost=10</bdi> تنفّذ 1,024 جدولة مفاتيح؛ و <bdi>cost=14</bdi> تنفّذ 16,384.
تجزئة bcrypt تبدو هكذا:
$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
│ │ │ │
│ │ │ └─ 31-char base64 hash
│ │ └─ 22-char base64 salt
│ └─ cost factor (12)
└─ algorithm identifier ($2b$ = bcrypt v2)
التنسيق ذاتي الوصف: تقرأ <bdi>verify()</bdi> التكلفة والملح من السلسلة المُخزَّنة، دون الحاجة إلى أعمدة منفصلة.
السلبيات حقيقية. بصمة bcrypt الذاكرية تبلغ نحو 4 KiB؛ صغيرة بما يكفي ليُشغِّل GPU راقٍ آلاف نوى bcrypt بالتوازي. كما أن bcrypt يقطع المُدخل بصمت عند 72 بايت. عبارة مرور بطول 100 حرف تتمتع بالأمان نفسه لأول 72 بايت منها فقط. الحد الأقصى للتكلفة هو 31، لكن أي قيمة فوق ~16 تبدأ في الإضرار بزمن استجابة تسجيل الدخول على عتاد استهلاكي.
scrypt: رائد صعوبة الذاكرة
نُشر scrypt سنة 2009 بواسطة Colin Percival لخدمة النسخ الاحتياطي Tarsnap، وقُنِّن في RFC 7914 سنة 2016. قدّم فكرة صعوبة الذاكرة: الخوارزمية تملأ مخزنًا كبيرًا ببيانات شبه عشوائية، ثم تقرأ من مواقع عشوائية، مما يُجبر أي تنفيذ على تخصيص الذاكرة فعلًا.
يأخذ scrypt ثلاث معاملات:
- N: تكلفة المعالج/الذاكرة (يجب أن تكون قوة لـ 2)
- r: حجم الكتلة بالبايت (مضاعف على الذاكرة وجولات الخلط)
- p: التوازي (حسابات مستقلة، تُستخدم غالبًا لتوسيع وقت المعالج دون توسيع الذاكرة)
استخدام الذاكرة يقارب <bdi>128 × N × r</bdi> بايت. مع توصية OWASP <bdi>N=2^17, r=8</bdi>، يكون الحساب <bdi>128 × 131072 × 8 = 134,217,728</bdi> بايت؛ أي 128 MiB لكل تجزئة بالضبط.
scrypt هو أيضًا دالة اشتقاق مفتاح، وليس فقط تجزئة كلمات مرور. يُستخدم في محافظ العملات المشفرة، وتشفير القرص الكامل، وأصل إثبات العمل لـ Litecoin. هذا الدور المزدوج مفيد عندما تحتاج إلى تخزين كلمات المرور واشتقاق المفاتيح في مكتبة واحدة.
Argon2 (id/i/d): الفائز في مسابقة تجزئة كلمات المرور
استمرت مسابقة تجزئة كلمات المرور (Password Hashing Competition) من 2013 إلى 2015، وقيّمت 24 خوارزمية مرشحة من حيث صعوبة الذاكرة، ومقاومة القنوات الجانبية، وبساطة التنفيذ. فاز Argon2. وقُنِّن في RFC 9106 سنة 2021.
لـ Argon2 ثلاثة أنواع. الفروقات تعود إلى كيفية معالجة عناوين الذاكرة أثناء الخلط:
- Argon2d يستخدم عناوين ذاكرة معتمدة على البيانات. يُعظِّم مقاومة هجمات GPU و ASIC لكنه يُسرِّب معلومات عبر قنوات توقيت الكاش الجانبية. مناسب لإثبات عمل العملات المشفرة، لا للمصادقة.
- Argon2i يستخدم عناوين مستقلة عن البيانات. آمن من القنوات الجانبية، لكنه أضعف قليلًا أمام هجمات المقايضة على GPU.
- Argon2id هجين: النصف الأول من المرور الأول يستخدم فهرسة Argon2i (آمن من القنوات الجانبية)، وما تبقى يستخدم فهرسة Argon2d (مقاوم لـ GPU). يوصي RFC 9106 صراحةً بـ Argon2id لتجزئة كلمات المرور، وكذلك يفعل OWASP.
يأخذ Argon2 ثلاث معاملات:
- m: الذاكرة بـ KiB
- t: تكلفة الزمن (عدد المرورات على مخزن الذاكرة)
- p: التوازي (عدد المسارات المعالجة بالتزامن)
تجزئة Argon2id تستخدم صيغة سلسلة PHC وتبدو كالتالي:
$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG
كما في bcrypt، كل المعاملات مُضمَّنة في السلسلة، فلا يحتاج <bdi>verify()</bdi> إلى جدول معاملات.
معاملات OWASP 2026 الموصى بها
OWASP Password Storage Cheat Sheet هو المرجع المعياري. الأرقام أدناه تطابق توجيهاتها الحالية. هي محافظة، مُحجَّمة لخادم ويب نموذجي بميزانية زمن استجابة تسجيل دخول من 100 إلى 500 ms؛ ومع ذلك يجب أن تقيس الأداء على عتادك قبل النشر.
معاملات Argon2id: الخيار الأول
توصية OWASP الأساسية: <bdi>m=19456</bdi> (<bdi>19 MiB</bdi>)، <bdi>t=2</bdi>، <bdi>p=1</bdi>.
إن كان لديك مساحة RAM أكبر على الخادم، يمكنك تحويل العمل بين الذاكرة والزمن. ينشر RFC 9106 ملفات تعريف معادلة؛ ويوصي OWASP بأي من هذه:
| memoryCost (m) | timeCost (t) | parallelism (p) | الذاكرة لكل تجزئة |
|---|---|---|---|
| 47104 | 1 | 1 | 46 MiB |
| 19456 | 2 | 1 | 19 MiB (الأساس) |
| 12288 | 3 | 1 | 12 MiB |
| 9216 | 4 | 1 | 9 MiB |
| 7168 | 5 | 1 | 7 MiB |
قاعدة الإبهام للضبط. اختر <bdi>m</bdi> أولًا بناءً على ميزانيتك للذاكرة عند ذروة تسجيل الدخول المتزامن. إذا توقّعت 100 تسجيل دخول متزامن ولديك 4 GiB فائضة، فهذا 40 MiB لكل تجزئة. ثم زِد <bdi>t</bdi> حتى يأخذ التحقق الواحد بين 100 و 500 ms على معالج الإنتاج لديك. اترك <bdi>p=1</bdi> ما لم يكن لديك سبب معيّن متعدد النوى لتغييرها (معظم أُطر الويب تخصّص خيطًا لكل طلب أصلًا).
معاملات scrypt: عند عدم توفر Argon2
توصية OWASP: <bdi>N=2^17</bdi> (<bdi>131072</bdi>)، <bdi>r=8</bdi>، <bdi>p=1</bdi>، وتستخدم 128 MiB لكل تجزئة.
إذا كان 128 MiB لكل تسجيل دخول متزامن أكبر من اللازم لخادمك، يسمح OWASP بملفات تعريف أضعف:
| N | r | p | الذاكرة لكل تجزئة |
|---|---|---|---|
| 2^17 | 8 | 1 | 128 MiB (المفضل) |
| 2^16 | 8 | 1 | 64 MiB |
| 2^15 | 8 | 1 | 32 MiB |
<bdi>N</bdi> يجب أن تكون قوة لـ 2. زيادة <bdi>r</bdi> ترفع الذاكرة وعمل المعالج بشكل متناسب؛ زيادة <bdi>p</bdi> ترفع عمل المعالج دون رفع الذاكرة لكل نسخة. لتجزئة كلمات المرور، اترك <bdi>r</bdi> و <bdi>p</bdi> على القيم الافتراضية واضبط <bdi>N</bdi> فقط.
bcrypt: عامل التكلفة 10+ للأنظمة القديمة فقط
لم يعد OWASP يوصي بـ bcrypt للمشاريع الجديدة، لكنه لا يزال موجودًا في كل مكان؛ Devise، Spring Security، ASP.NET Identity، وعدد لا يُحصى من أنظمة المصادقة المنزلية تعتمده افتراضيًا.
إذا كنت عالقًا مع bcrypt، فالقواعد:
- الحد الأدنى لعامل تكلفة bcrypt: 10. أقل من 10 سريع بما يكفي ليُنهي GPU واحدة قاعدة بيانات مسرّبة في أيام.
- الموصى به: من 12 إلى 14، حسب العتاد. على خادم x86 حديث، يأخذ
<bdi>cost=12</bdi>نحو 250 ms لكل تجزئة؛ و<bdi>cost=13</bdi>يأخذ 500 ms. - استهدف من 100 إلى 300 ms لكل تحقق على عتاد الإنتاج لديك. قِس، لا تخمّن.
- تذكّر حد الإدخال البالغ 72 بايت. إذا كان للمستخدمين اختيار عبارات مرور، فجزِّئها مسبقًا بـ SHA-256 (راجع الأسئلة الشائعة).
مقاومة bcrypt لـ GPU محدودة ببصمته الذاكرية البالغة 4 KiB. لا يوجد عامل تكلفة لـ bcrypt سيُجاري صعوبة ذاكرة Argon2id؛ اختر Argon2id عندما تستطيع.
مرجع عملي: على خادم EPYC طراز 2024، يعمل <bdi>bcrypt(cost=12)</bdi> في نحو 250 ms؛ على حاسب محمول راقٍ، أقرب إلى 350 ms. إذا خرجت أرقامك عن نطاق 100 إلى 500 ms بمقدار رتبة من العشر، فتحقّق ما إذا كانت مكتبتك تستخدم bcrypt الأصلي فعلًا، أم أنها تنزلق إلى polyfill بطيء بـ JavaScript (بعض حزم الـ bundler تُجرِّد الاعتماديات الأصلية في بناءات serverless).
PBKDF2: مسار التوافق مع FIPS-140
PBKDF2 (RFC 8018) هو خوارزمية الملاذ الأخير في توجيهات الأمان. هو أقدم من bcrypt، وليس صعب الذاكرة، ويسقط أمام هجمات GPU أسرع من أي من الثلاث المذكورة أعلاه. لكنه البدائية الوحيدة لتجزئة كلمات المرور المعتمدة من FIPS-140، وهذا يهم للحكومة الفيدرالية، والرعاية الصحية ضمن HIPAA، وبعض النشرات المالية.
عندما تحتاج إلى PBKDF2، استخدم:
- HMAC-SHA-256 كـ PRF (لا تستخدم SHA-1؛ ولا تستخدم SHA-256 الخام بدون HMAC)
- 600,000 تكرار كحد أدنى (خط أساس OWASP 2026)
- ملح عشوائي بطول 16 بايت على الأقل لكل كلمة مرور
إذا لم يكن FIPS منطبقًا عليك، فضِّل Argon2id. تصميم PBKDF2 ذو المخرج الثابت والذاكرة الثابتة يعني أن كل دولار من سيليكون GPU يشتريه المهاجم يُترجَم مباشرة إلى تخمينات أكثر للكلمة في الثانية.
NIST SP 800-63B يصف PBKDF2-HMAC بأنه “معتمَد” لتجزئة كلمات المرور لكنه لا يصل إلى التوصية به فوق البدائل الصعبة الذاكرة. اقرأ ذلك على هذا النحو: NIST يسمح بـ PBKDF2 لأن إلغاءه سيُبطل كل نشر حكومي قديم، لا لأنه الخيار الأفضل لمشروع جديد.
إطار القرار: أي خوارزمية تختار؟
جدول المقارنة
| البُعد | bcrypt | scrypt | Argon2id | PBKDF2 |
|---|---|---|---|---|
| صعب الذاكرة | لا | نعم | نعم | لا |
| مقاومة GPU | متوسطة | عالية | عالية جدًا | منخفضة |
| مقاومة القنوات الجانبية | متوسطة | متوسطة | عالية (id) | متوسطة |
| تعقيد المعاملات | 1 (cost) | 3 (N, r, p) | 3 (m, t, p) | 1 (iterations) |
| نضج المكتبات | ممتاز | جيد | جيد | ممتاز |
| حد طول الإدخال | 72 بايت | لا يوجد | لا يوجد | لا يوجد |
| التقنين | بحكم الواقع | RFC 7914 | RFC 9106 | RFC 8018 |
| حالة OWASP 2026 | للأنظمة القديمة فقط | بديل | الخيار الأول | FIPS فقط |
استخدم Argon2id افتراضيًا
لمشروع جديد؛ تطبيق ويب نموذجي، حزمة Node أو Python أو Go أو Rust أو JVM حديثة، ولا قيود FIPS؛ استخدم Argon2id بـ <bdi>m=19456, t=2, p=1</bdi>. تحصل على أقوى مقاومة لـ GPU والقنوات الجانبية المتاحة، وصيغة معاملات مُضمَّنة تنجو من ترقيات المكتبات، وبدون مفاجآت طول الإدخال. منظومة المكتبات ناضجة: argon2 على npm، argon2-cffi على PyPI، golang.org/x/crypto/argon2، صندوق argon2 على crates.io، كلها مُصانة ومُقاسة الأداء.
متى تختار scrypt أو bcrypt بدلًا منه؟
اختر scrypt عندما يكون Argon2 غير متاح في بيئة التشغيل لديك (نادر فعلًا في 2026؛ حتى Cloudflare Workers و Deno صار لديهم الآن)، أو عندما يكون لديك أصلًا نظام إنتاج يستخدم scrypt وتفوق تكلفة الهجرة الفارقَ الأمنيَّ. scrypt لا يزال خوارزمية متينة؛ هو فقط يفتقر إلى صقل القنوات الجانبية في Argon2id.
اختر bcrypt عندما تُصين نظامًا قديمًا، ولديك متطلب صارم لتقليل الاعتماديات (لا كود أصلي ولا حزم إضافية)، وحد الإدخال البالغ 72 بايت مقبول لقاعدة مستخدميك. bcrypt نُشر بمقياس الإنترنت لعقدين؛ أنماط فشله مفهومة جيدًا.
اختر PBKDF2 عندما يطلب المنظِّم ذلك. هذا هو السبب الوحيد. إذا كان مدقّقك يقبل Argon2id (عدد متزايد منهم يقبله الآن لأعباء العمل خارج FIPS)، فاستخدم Argon2id.
أخطاء شائعة يجب تجنبها
معظم تسريبات تخزين كلمات المرور في العقد الماضي تعود إلى مجموعة صغيرة من الأخطاء الهندسية المتكررة. لا شيء منها غريب؛ كلها تُلتقَط بمراجعة كود المصادقة لديك ووضع القائمة التالية أمامك.
- تجزئة كلمات المرور بـ SHA-256 أو MD5 الخام. هذا أكبر فشل منفرد في تخزين كلمات المرور. راجع MD5 مقابل SHA-256 لمعرفة لماذا هذه الخوارزميات خاطئة لكلمات المرور.
- إعادة استخدام ملح عام واحد لكل المستخدمين. الملح يجب أن يكون فريدًا لكل سجل. Argon2 و bcrypt يولّدان واحدًا لك؛ لا تتجاوز ذلك.
- ضبط زمن التجزئة دون 50 ms. بدّلت أمنًا بزيادة سرعة لا يستطيع أي مستخدم إدراكها. استهدف 100 إلى 500 ms.
- ضبط زمن التجزئة فوق ثانية واحدة. أنشأت ناقل حرمان من الخدمة ضد نقطة تسجيل الدخول لديك. حُدّ عند ~500 ms.
- تجزئة كلمات المرور في جانب العميل وإرسال البصمة إلى الخادم. التجزئة الآن هي كلمة المرور. أي شخص يسرق قاعدة البيانات يستطيع المصادقة دون الحاجة إلى عكسها أبدًا. جزِّئ على الخادم دائمًا.
- تخزين معاملات الخوارزمية في عمود منفصل. صيغة سلسلة PHC تُضمِّنها في التجزئة. استخدمها.
- تسجيل كلمات المرور أو التجزئات أثناء معالجة الأخطاء. كلاهما ملك المستخدم، لا ملك مُجمِّع السجلات لديك. امسحها في طبقة تحليل الطلب قبل أن تصل إلى أي مُسجِّل.
- معاملة استثناءات
<bdi>verify()</bdi>على أنها فشل في المصادقة. المكتبة التي ترمي استثناءً على تجزئة مُخزَّنة مشوّهة يجب أن تُظهر الخطأ، لا أن تنزلق بصمت إلى “كلمة المرور خاطئة”. ميِّز بين “كلمة المرور خاطئة” (إرجاع 401) و”التجزئة المُخزَّنة فاسدة” (إرجاع 500 واستدعاء المناوب).
التنفيذ في العالم الواقعي
Argon2id في Node.js
حزمة argon2 (روابط أصلية لتنفيذ المرجعي) هي الخيار المعياري على Node:
import argon2 from 'argon2';
// التجزئة عند التسجيل أو تغيير كلمة المرور
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 19456, // 19 MiB
timeCost: 2,
parallelism: 1,
});
// → '$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>'
// التحقق عند تسجيل الدخول
const ok = await argon2.verify(hash, candidate);
if (!ok) throw new Error('Invalid credentials');
// اكتشف المعاملات القديمة وأعد التجزئة عند تسجيل دخول ناجح
if (argon2.needsRehash(hash, { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1 })) {
const upgraded = await argon2.hash(candidate, {
type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1,
});
await db.users.update({ id: user.id }, { password_hash: upgraded });
}
خطوة <bdi>needsRehash</bdi> هي سرّ الهجرة طويلة المدى: كل تسجيل دخول ناجح يصير فرصة لترقية التجزئة المُخزَّنة إلى المعاملات الحالية، دون إزعاج المستخدم.
النمط نفسه في Python مع argon2-cffi:
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher(memory_cost=19456, time_cost=2, parallelism=1)
# التجزئة
stored = ph.hash(password)
# التحقق
try:
ph.verify(stored, candidate)
except VerifyMismatchError:
raise ValueError('Invalid credentials')
# إعادة التجزئة عند ترقية المعاملات
if ph.check_needs_rehash(stored):
stored = ph.hash(candidate)
في Go مع golang.org/x/crypto/argon2:
import (
"crypto/rand"
"golang.org/x/crypto/argon2"
)
func hashPassword(password string) ([]byte, []byte) {
salt := make([]byte, 16)
rand.Read(salt)
hash := argon2.IDKey([]byte(password), salt, 2, 19456, 1, 32)
return hash, salt
}
مكتبة Go القياسية لا تشحن مُرمِّزًا لصيغة PHC؛ إذا استخدمت بدائية <bdi>argon2.IDKey</bdi> مباشرةً، فأنت مسؤول عن ترميز المعاملات والملح إلى جانب التجزئة. معظم مشاريع Go تستخدم غلافًا مثل github.com/alexedwards/argon2id لذلك.
Rust مع صندوق argon2 اصطلاحي بالمثل:
use argon2::{Argon2, PasswordHasher, PasswordVerifier, password_hash::{SaltString, rand_core::OsRng}};
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default(); // Argon2id, m=19456, t=2, p=1 by default
let hash = argon2.hash_password(password.as_bytes(), &salt)?.to_string();
// On verify
let parsed = argon2::password_hash::PasswordHash::new(&hash)?;
argon2.verify_password(candidate.as_bytes(), &parsed)?;
في بيئات التشغيل الثلاث، السلسلة المُنتَجة قابلة للتبادل؛ تجزئة أُنشئت في Node تتحقّق بشكل نظيف في Python أو Rust. هذا التوافق العابر لبيئات التشغيل يجعل Argon2 رهانًا أأمن للهياكل متعددة اللغات من الأغلفة الخاصة بخوارزمية بعينها.
نمط الهجرة من bcrypt إلى Argon2id
نادرًا ما يُتاح لك مسح جدول المستخدمين والبدء من جديد. نمط الهجرة الصحيح هو نفسه المستخدم في قسم الهجرة من MD5 إلى bcrypt في الأسئلة الشائعة لمولّد التجزئة لدينا: ترقية ناعمة مدفوعة بتسجيل الدخول.
أضف عمودًا يتتبّع الخوارزمية:
ALTER TABLE users ADD COLUMN password_algo VARCHAR(16) NOT NULL DEFAULT 'bcrypt';
عند تسجيل الدخول، ادفع إلى المتحقّق المناسب:
async function verifyAndMaybeRehash(user, candidate) {
let ok;
if (user.password_algo === 'argon2id') {
ok = await argon2.verify(user.password_hash, candidate);
} else if (user.password_algo === 'bcrypt') {
ok = await bcrypt.compare(candidate, user.password_hash);
if (ok) {
// تحقق ناجح من النظام القديم → أعد التجزئة بـ Argon2id
const newHash = await argon2.hash(candidate, {
type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1,
});
await db.users.update({ id: user.id }, {
password_hash: newHash,
password_algo: 'argon2id',
});
}
}
return ok;
}
اضبط نافذة إنهاء من 6 إلى 12 شهرًا. أرسل بريدًا إلكترونيًا “كلمة مرورك مُخزَّنة بطريقة قديمة، يرجى تسجيل الدخول للترقية” عند العلامة 9 أشهر. بعد 12 شهرًا، الحسابات التي لا تزال على bcrypt تتطلب إعادة تعيين إجبارية لكلمة المرور عند تسجيل الدخول التالي. المستخدمون النشطون يُهاجَرون بشفافية؛ والحسابات الخاملة تواجه حدثًا واحدًا للاحتكاك.
النمط نفسه يعمل للهجرة من scrypt أو PBKDF2. الحالة الوحيدة التي تحتاجها هي عمود <bdi>password_algo</bdi>.
الفلفل (Pepper) وحدود الطول ومطبّات الترميز
بضع حواف حادة تعض النشرات الحقيقية:
الفلفل (Pepper). الفلفل سرّ على مستوى التطبيق يُضاف لكل كلمة مرور قبل التجزئة، يُخزَّن بمعزل عن قاعدة البيانات (في KMS أو متغير بيئة أو Hashicorp Vault). إذا تسرّبت قاعدة بياناتك ولم يتسرّب سرّ التطبيق، فالتجزئات المسرّبة غير قابلة للهجوم بدون الفلفل. طبّقه كـ HMAC، لا تسلسلًا:
import { createHmac } from 'crypto';
const peppered = createHmac('sha256', process.env.PEPPER).update(password).digest();
const hash = await argon2.hash(peppered, { type: argon2.argon2id, /* ... */ });
دوِّر الفلفل نادرًا (يتطلب إعادة تجزئة) لكن ادعَم التدوير بالإصدار: <bdi>PEPPER_V2</bdi>، مع رجوع إلى <bdi>PEPPER_V1</bdi> عند التحقق.
حد 72 بايت في bcrypt. إذا لزمك استخدام bcrypt وتريد دعم كلمات مرور بأي طول، جزِّئ مسبقًا بـ SHA-256 ورمِّز بـ base64 (لتجنّب بايتات NUL المُضمَّنة التي يتعامل معها bcrypt بطريقة غير متسقة):
import { createHash } from 'crypto';
const prepped = createHash('sha256').update(password, 'utf8').digest('base64');
const hash = await bcrypt.hash(prepped, 12);
التحويل نفسه <bdi>prepped</bdi> يجب أن يعمل عند التحقق. وثِّق هذا في كود المصادقة لديك بتعليق كبير؛ سوف يشكرك “أنت المستقبلي” على “أنت الحالي”.
التطبيع UTF-8. السلسلة "<bdi>café</bdi>" يمكن ترميزها إما <bdi>c-a-f-é</bdi> (4 نقاط رمزية، NFC) أو <bdi>c-a-f-e + combining acute</bdi> (5 نقاط رمزية، NFD). تبدوان متطابقتين لكنهما تنتجان تجزئات مختلفة. طبِّع دائمًا إلى NFC قبل التجزئة:
const normalized = password.normalize('NFC');
هذه المشكلة تعض لوحات مفاتيح الجوّال والنسخ واللصق من ملفات PDF أكثر مما تتوقّع.
لا تُجزِّئ مسبقًا في جانب العميل أبدًا. التجزئة المحسوبة في العميل والمرسلة إلى الخادم هي كلمة المرور الجديدة. أي شخص يقرأ قاعدة بياناتك يستطيع المصادقة. جزِّئ على الخادم، نقطة. JWT لا يغيّر هذا؛ راجع كيفية فك تشفير رمز JWT لمعرفة ما الذي يصادق عليه JWT وما الذي لا يصادق عليه.
قِس الأداء على عتاد الإنتاج، لا على حاسبك المحمول. حاسب Intel الجيل 13 يشغّل Argon2id بـ <bdi>m=19456, t=2, p=1</bdi> في نحو 35 ms. المعاملات نفسها على نسخة EC2 من نوع <bdi>t3.small</bdi> تأخذ أقرب إلى 180 ms؛ على Raspberry Pi 4، أكثر من 600 ms. اختر العتاد الذي سيشغّل الإنتاج فعلًا، وقِس 1,000 تحقق، واضبط من الوسيط. تباين زمن استجابة تسجيل الدخول من حاويات serverless الباردة يستحق القياس أيضًا؛ بدايات Lambda الباردة قد تضيف 200 إلى 800 ms غير مرتبطة بالتجزئة.
الأسئلة الشائعة
ما الفرق بين تجزئة كلمات المرور والتشفير؟
التجزئة باتجاه واحد: تحسب بصمة بطول ثابت لا يمكن عكسها لاسترداد المُدخل. التشفير باتجاهين: بالمفتاح الصحيح، يمكنك فك التشفير للعودة إلى الأصل. كلمات المرور يجب أن تُجزَّأ، لا أن تُشفَّر. الخادم يجب ألا يستطيع استرداد كلمة مرور أي مستخدم؛ هكذا لا يصبح تسريب قاعدة البيانات تسريب اعتمادات.
لمَ لا أستطيع استخدام SHA-256 لكلمات المرور؟
صُمِّم SHA-256 للسرعة. GPU حديثة تحسب 22 مليار تجزئة SHA-256 في الثانية، مما يعني أن كلمة مرور من 8 أحرف صغيرة من قاعدة بيانات مسرّبة تسقط في دقائق. تجزئات كلمات المرور تحتاج إلى ثلاث خصائص يفتقر إليها SHA-256: تنفيذ بطيء عمدًا، وملح لكل سجل، وصعوبة الذاكرة. مبدأ المقايضة هو نفسه المشروح في توجيه “لا تستخدم MD5 للأمان” في مولّد التجزئة لدينا؛ ويمكنك قراءة المزيد عن كيفية تحويل المهاجمين تجزئات ضعيفة إلى نص صريح في إنتروبيا كلمة المرور.
هل لا يزال bcrypt آمنًا في 2026؟
bcrypt نفسه لم يُكسَر. جدولة المفاتيح المبنية على Blowfish لا تزال سليمة تشفيريًا. ما تغيّر هو نموذج التهديد: GPU و ASIC تجعل افتقار bcrypt إلى صعوبة الذاكرة ضعفًا ذا مغزى مقارنة بـ Argon2id. موقف OWASP في 2026 هو أن bcrypt مقبول للأنظمة القديمة بتكلفة ≥ 10، لكن المشاريع الجديدة يجب أن تختار Argon2id.
Argon2i مقابل Argon2d مقابل Argon2id؛ أيها أستخدم؟
استخدم Argon2id. يحدّده RFC 9106 كالنوع الموصى به لتجزئة كلمات المرور. Argon2i مستقل عن البيانات (آمن من القنوات الجانبية لكن أضعف ضد هجمات المقايضة على GPU). Argon2d معتمد على البيانات (قوي ضد GPU لكنه عرضة لقنوات توقيت الكاش الجانبية). Argon2id هجين يحصل على الخاصيتين بسعر واحد.
كيف أختار معاملات Argon2id لتطبيقي؟
ابدأ بخط أساس OWASP: <bdi>m=19456, t=2, p=1</bdi>. ثم قِس على معالج الإنتاج لديك واضبط:
- حدّد ميزانيتك للذاكرة لكل تسجيل دخول (مثلًا 50 MiB عند ذروة التزامن).
- اضبط
<bdi>m</bdi>على تلك القيمة أو أقل. - شغّل
<bdi>argon2.hash()</bdi>في حلقة وقِس زمن الجدار. - زِد
<bdi>t</bdi>حتى يكون الوسيط بين 100 و 500 ms.
اترك <bdi>p=1</bdi> ما لم تكن قد أجريت تشخيصًا وعرفت أن التوازي متعدد المسارات يساعد بيئة تشغيلك. لخوادم المصادقة عالية الحركة، الانحياز نحو <bdi>t</bdi> أعلى و <bdi>m</bdi> أقل غالبًا يعطي مساحة RAM أفضل.
ما هو حد 72 بايت في bcrypt وكيف أتعامل مع عبارات المرور الطويلة؟
bcrypt يُغذّي مدخلاته إلى جدولة مفاتيح Blowfish، التي تقطع عند 72 بايت. عبارة مرور بطول 150 حرفًا تتمتع بنفس أمان أول 72 بايت منها؛ والباقي يُتجاهل. الإصلاح هو التجزئة المسبقة بـ SHA-256 (32 بايت) أو SHA-512 (64 بايت)، ترميز البصمة بـ base64 لتجنّب بايتات NUL، وتغذية ذلك إلى bcrypt. Argon2id و scrypt ليس لديهما هذا الحد؛ يقبلان مدخلًا طويلًا اعتباطيًا مباشرة.
هل أستطيع الهجرة من bcrypt إلى Argon2 دون إجبار إعادة تعيين كلمات المرور؟
نعم. النمط هو: تخزين كلتا الخوارزميتين خلف عمود <bdi>password_algo</bdi>، الدفع بالتحقق إلى المكتبة المناسبة، وعند كل تحقق bcrypt ناجح، أعد التجزئة فورًا بـ Argon2id وحدِّث الصف. المستخدمون النشطون يُهاجَرون بصمت ضمن إيقاع تسجيل دخولهم الطبيعي. اضبط نافذة إنهاء من 6 إلى 12 شهرًا للحسابات الخاملة، ثم أجبِر إعادة تعيين كلمة المرور لأي سجل لا يزال على bcrypt. النمط نفسه يعمل لأي هجرة من خوارزمية إلى خوارزمية.
هل لا يزال PBKDF2 خيارًا جيدًا في 2026؟
فقط حين يفرض التوافق مع FIPS-140 ذلك؛ نموذجي في الحكومة الفيدرالية، والرعاية الصحية المنظَّمة (HIPAA)، وبعض الأنظمة المالية. استخدم HMAC-SHA-256 كـ PRF مع 600,000 تكرار على الأقل. PBKDF2 ليس صعب الذاكرة، لذا يسقط أمام هجمات GPU أسرع من Argon2id عند ميزانيات زمن استجابة متكافئة. إذا لم يكن FIPS منطبقًا، اختر Argon2id وتجاوز رياضات الامتثال.
إجابة تجزئة كلمات المرور لعام 2026 قصيرة: الافتراضي Argon2id بمعاملات خط أساس OWASP، رجوع إلى scrypt إذا لم يكن Argon2 متاحًا، إبقاء bcrypt فقط حيث يطلب الموروث، وحجز PBKDF2 للأنظمة المُلزَمة بـ FIPS. اقرن التجزئة بملح لكل سجل (كل مكتبة حديثة تتعامل مع هذا تلقائيًا)، وفلفل على مستوى التطبيق مُخزَّن خارج قاعدة البيانات، وحلقة إعادة تجزئة مدفوعة بتسجيل الدخول تتيح لك رفع عوامل العمل مع تطور العتاد.
ولِّد مجموعة كلمات مرور تمثيلية باستخدام مولّد كلمات مرور عشوائية، وقِس مسار التحقق لديك على معالج الإنتاج، واكتب المعاملات في ملف ثوابت ليعرف المهندس التالي بالضبط ما الذي ينبغي رفعه في 2028. السياق الأمني الكامل؛ TLS وإدارة الجلسات وتحديد المعدّل و MFA؛ يعيش في دليل أساسيات أمان الويب. اختر التجزئة المناسبة لتطبيقك.