حفظ أسرار تطبيقات Windows - تجنّب إعداد النصّ الصريح بـ DPAPI

· آخر تحديث: · · تطوير Windows, الأمان, DPAPI, C# / .NET, Win32

سجل التعديلات (2 تحديثات، آخر تحديث 2 Sep، 2026)

سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.

أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621374)

هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.

小村 豪 (2026). حفظ أسرار تطبيقات Windows - تجنّب إعداد النصّ الصريح بـ DPAPI. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621374 https://comcomponent.com/ar/blog/2026/03/16/000-windows-app-secret-storage-best-practices-dpapi/

DOI (أحدث نسخة)
10.5281/zenodo.21621374
DOI (هذه النسخة)
10.5281/zenodo.22240842

في المقال السابق «قائمة تحقّق للحدّ الأدنى من الأمان في تطوير تطبيقات Windows» كتبتُ الخطّ الأدنى: «لا تضع الأسرار في الشيفرة المصدريّة أو في إعداد نصّ صريح»، و«في Win32 / .NET استخدم DPAPI / ProtectedData».

هذه المرّة أتعمّق أكثر في «استخدام DPAPI ليصير الأمر على الأقلّ أفضل من النصّ الصريح».

الهدف تطبيقات Windows من هذا النوع.

  • تطبيقات سطح مكتب WPF / WinForms / WinUI
  • عميل Windows بـ C# / .NET
  • تطبيقات تميل إلى حفظ معلومات اعتماد الاتّصال أو رموز API في ملفّ إعداد محلّيّ

ما نعالجه هنا تصميم واقعيّ من أجل «سرّ لا مفرّ من حفظه محلّيّاً، على الأقلّ لا يُترَك نصّاً صريحاً في appsettings.json». ليس حديث «دفاع كامل ينتصر على أيّ مهاجم». إن حُشي النقاش أكثر من اللازم ابتعد عن نقاش أمن واقعيّ.

1. الخلاصة أوّلاً

في العمل يسهل الفهم بهذا الترتيب.

  1. لا تعطِ العميل أصلاً سرّاً طويل العمر
    • قدّم مصادقة Windows، أو المصادقة المتكاملة، أو تسجيل الدخول التفاعليّ، أو إدارة الأسرار في جهة الخادم
  2. إن لزم الحفظ المحلّيّ حتماً، فلا تضعه نصّاً صريحاً
    • على Windows اجعل DPAPI / ProtectedData المرشّح الأوّل
  3. تطبيقات سطح المكتب العاديّة تتّخذ DataProtectionScope.CurrentUser أساساً
    • استخدامات LocalMachine ضيّقة جدّاً
  4. DPAPI لا يحمي حتّى «حالة اختراق الجهاز بالكامل»
    • الشيفرة التي تعمل بصلاحيّات المستخدم نفسه تستطيع في الأساس فكّ تشفير ما يستطيع ذلك المستخدم فكّ تشفيره

وأهمّ نقطة في هذا المقال هي هذه.

«على أيّ حال يجب حفظ المفتاح السرّيّ في مكان ما، أليس النصّ الصريح و DPAPI سواء من جهة الأمن؟»

هذا نصف صحيح، والنتيجة خاطئة.

  • AES ذاتيّ + وضع المفتاح في نفس التطبيق أو نفس الإعداد قريب جدّاً من النصّ الصريح
  • لكن DPAPI يقرّب إدارة المفاتيح إلى نظام التشغيل، ويربط جهة فكّ التشفير بـ«مستخدم Windows ذلك» أو بـ«ذلك الحاسوب»
  • لذلك تتغيّر القوّة كثيراً أمام حوادث مثل تسرّب ملفّ الإعداد وحده، أو نقله إلى حاسوب آخر، أو إرساله خطأ، أو تسرّب نسخة احتياطيّة، أو دخوله المستودع

أي أنّه حتّى إن بدا الأمر سواء بالنظر إلى التجريد «المفتاح في مكان ما»، فـ«من يستطيع استخدامه، وفي أيّ سياق، وبأيّ سهولة» مختلف تماماً.

القول إنّ وضع المفتاح تحت ممسحة الباب وتسليمه بعد التحقّق من الهويّة في غرفة الإدارة شيء واحد قول متسرّع قليلاً.

«المفتاح في مكان ما» وحده لا يجعل الأمر سواءمخطّط يبيّن النقطة المركزيّة في المقال: بالنظر إلى تجريد أنّ المفتاح في مكان ما يبدو النصّ الصريح و DPAPI سواء، لكن من يستطيع الاستخدام وفي أيّ سياق وبأيّ سهولة مختلف تماماً.إن نُظر إلى هذا وحدهإن نُظر إلى هذاالمفتاح في مكان ما (تجريد)يبدو سواءمن يستطيع، وفي أيّ سياق، وبأيّ سهولةمختلف تماماً

الشكل 1: في التجريد يبدوان سواء، لكن «من يستطيع الاستخدام وفي أيّ سياق» يفصل النصّ الصريح عن DPAPI.

خريطة المعرفة لهذه المقالة

يعالج هذا المقال حفظ أسرار تطبيقات سطح مكتب Windows (مثل سلاسل الاتّصال ورموز API) بأمان أكبر من إعداد النصّ الصريح باستخدام DPAPI (ProtectedData). الحفظ بالنصّ الصريح والتشفير الذاتي الذي يضع المفتاح في نفس مكان النصّ المشفَّر يسرّبان السرّ فور تسرّب ملفّ الإعداد وحده، بينما يخفّف DPAPI ذلك بربط جهة فكّ التشفير بمستخدم Windows أو بالحاسوب. تطبيقات سطح المكتب العاديّة تتّخذ نطاق CurrentUser أساساً، ويُضيَّق LocalMachine إلى حالات مثل خدمة Windows موثوقة لغرض واحد. غير أنّ إعادة تعيين كلمة المرور وإعادة إنشاء الملفّ الشخصيّ ونسخ النصّ المشفَّر وحده إلى حاسوب آخر أسباب لفشل فكّ التشفير، وبالمقابل إن وُجد ملفّ شخصيّ جوّال انتقلت مادّة المفتاح معه فيمكن فكّ التشفير من حاسوب آخر. إن أردت حفظ زوج اسم مستخدم وكلمة مرور، أو مشاركة السرّ عبر عدّة أجهزة، فأنسب من DPAPI هما Credential Locker أو Credential Manager.

خريطة معرفة حماية أسرار تطبيقات Windows بـ DPAPIمخطّط يبيّن أنّ DPAPI يبدّل جهة فكّ التشفير بنطاقَي CurrentUser و LocalMachine، وأنّه يخفّف مخاطر التسرّب الناجمة عن الحفظ بالنصّ الصريح والتشفير الذاتي، والتفريق بينه وبين Credential Locker و Credential Manager، ومطبات التشغيل التي تسبّب فشل فكّ التشفيرقد يسبّبيخفّفقد يسبّبغير موصى به لـيُكوَّن بـيُكوَّن بـموصى به لـموصى به لـغير موصى به لـقد يسبّبموصى به لـغير موصى به لـموصى به لـموصى به لـيشترطلا يتوافق معموصى به لـقد يسبّبقد يسبّبقد يسبّبيمنعDPAPIأسرار التطبيقحفظ الأسرار بالنصّ الصريحتسرّب السرّ بتسرّب ملفّ الإعداد وحدهتشفير ذاتيّ يضع المفتاح في نفس مكان النصّ المشفَّرسرّ طويل العمر مشترك لكلّ العملاءDataProtectionScope.CurrentUserDataProtectionScope.LocalMachineتطبيق سطح مكتب لمستخدم تفاعليّخدمة Windowsفشل فكّ تشفير DPAPIزوج اسم مستخدم وكلمة مرورCredential Locker (PasswordVault)Credential Manager (CredWrite / CredRead)حساب Microsoftمشاركة السرّ عبر أجهزة أو مستخدمين متعدّدينإعادة تعيين كلمة المرور من المسؤولإعادة إنشاء الملفّ الشخصيّنسخ النصّ المشفَّر وحده إلى حاسوب آخرالملفّ الشخصيّ الجوّال (roaming profile)

في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 21، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle

2. لماذا إعداد النصّ الصريح خطر

سبب خطر الحفظ بالنصّ الصريح أوضح بكثير من نظريّة التشفير. في العمل يتسرّب تقريباً عبر مسارات كهذه.

  • إدخال ملفّ الإعداد كما هو إلى Git
  • دخول ملفّ الإعداد كاملاً في أرشيف ZIP للتحقيق في عطل
  • طلب إرفاق ملفّ الإعداد في استعلام الدعم
  • قراءة طرف ثالث عبر نسخة احتياطيّة أو مشاركة ملفّات
  • ظهور سلسلة الاتّصال أو الرمز كما هو في السجلّ
  • قراءة موظّف سابق أو مستخدم آخر للملفّ على نفس الجهاز

النصّ الصريح يفقد السرّيّة لحظة قراءته من طرف ثالث.

  • فُتح الملفّ فانتهى الأمر
  • أمكن نسخه فانتهى الأمر
  • أُرفق بالبريد فانتهى الأمر
  • بقي في المستودع فصار عبئاً شبه دائم

لا يحتاج المهاجم حتّى إلى براعة. أن يُفتَح بمحرّر نصوص وحده ضعف كبير.

النصّ الصريح يفقد السرّيّة لحظة قراءتهمخطّط يبيّن أنّ ملفّ الإعداد إن قرأه طرف ثالث عبر مسارات يوميّة مثل دخول Git أو أرشيف التحقيق أو تسرّب النسخة الاحتياطيّة أو الإرفاق، فقد النصّ الصريح سرّيّته.دخول Git وأرشيف التحقيق والإرفاق والنسخة الاحتياطيّةيُقرأ ملفّ الإعدادتُفقَد السرّيّة لحظة قراءة طرف ثالثلا يحتاج المهاجم حتّى إلى براعة

الشكل 2: ضعف النصّ الصريح أنّ السرّيّة تُفقَد لحظة قراءة طرف ثالث عبر مسارات حوادث يوميّة.

3. الجواب على «المفتاح سيُحفَظ في مكان ما، أليس الأمر سواء؟»

هذا السؤال وجيه. وإن أُجيب عنه باستخفاف صار مقال الأمن ضبابيّاً دفعة واحدة.

الجواب: بمعنى «يلزم مفتاح في مكان ما» نعم، وبمعنى «إذن هما سواء» لا.

3.1. ما الذي يستوي، وما الذي يختلف

صحيح أنّ التشفير يحتاج في النهاية إلى نوع من root of trust. أي أنّ التشفير يحتاج في النهاية إلى نقطة انطلاق للثقة.

لكن الفرق الأمنيّ يُحسَم بهذه الثلاث.

  • هل يحمل التطبيق المفتاح مباشرة
  • بأيّ جهة يرتبط المفتاح
  • هل يمكن فكّ التشفير إن سُرق الملفّ وحده

إن لخّصنا هذا الفرق في جدول، فهو كالتالي.

الأسلوب قُرئ ملفّ الإعداد نُقل الملفّ وحده إلى حاسوب آخر قرأه مستخدم آخر على نفس الحاسوب شيفرة تعمل بصلاحيّات المستخدم نفسه
نصّ صريح يتسرّب في الحال يتسرّب كما هو يتسرّب كما هو يُقرأ طبعاً
تشفير ذاتيّ + المفتاح في نفس الإعداد / الثنائيّ يتسرّب إلى حدّ بعيد يتسرّب إلى حدّ بعيد يتسرّب إلى حدّ بعيد يمكن فكّ التشفير طبعاً
DPAPI + CurrentUser لا يُقرأ فوراً من الملفّ وحده يصعب فكّ التشفير عادةً يصعب فكّ التشفير عادةً يمكن فكّ التشفير
DPAPI + LocalMachine لا يُقرأ فوراً من الملفّ وحده يصعب فكّ التشفير عادةً خارج ذلك الحاسوب يمكن فكّ التشفير على نطاق واسع على نفس الحاسوب يمكن فكّ التشفير

المهمّ هنا أنّ DPAPI يفصل «يمكن قراءة الملفّ» عن «يمكن استخدام السرّ».

ما يلزم لفكّ التشفير ── يبقى في جهة نظام التشغيل ولا يدخل النصّ المشفَّرحماية CurrentUserحماية LocalMachineالمفتاح الرئيسيّ للمستخدمعند الحماية بـ CurrentUserالمفتاح الرئيسيّ للحاسوبعند الحماية بـ LocalMachineالنصّ المشفَّريدخل ملفّ الإعداد / عمود قاعدة البيانات / أرشيف التحقيق= ما يمكن نقلهشيفرة تعمل بصفة ذلك المستخدم← يمكن فكّ التشفيرمستخدم آخر على نفس الحاسوب← لا يمكن فكّ التشفيرشيفرة على نفس الحاسوب← يمكن فكّ التشفير حتّى من مستخدم آخرنسخ النصّ المشفَّر وحده إلى حاسوب آخر← لا يمكن فكّ التشفيرالانتقال مع الملفّ الشخصيّ الجوّال← تنتقل مادّة المفتاح أيضاً فيمكن فكّ التشفير (القسم 6.5)

الشكل 3: النصّ المشفَّر وحده في الجهة التي يمكن نقلها، والمفتاح الرئيسيّ اللازم لفكّ التشفير يبقى في جهة نظام التشغيل. غير أنّ نطاق وصول LocalMachine هو الحاسوب كلّه، ولا يُقصَى مستخدم آخر على نفس الحاسوب

في النصّ الصريح هذان الشيءان واحد. إن أمكن قراءة الملفّ أمكن قراءة السرّ.

أمّا في DPAPI، فعلى الأقلّ مع CurrentUser، يلزم فكّ التشفير:

  • بصفة مستخدم Windows ذلك
  • في سياق Windows ذلك
  • عبر آليّة حماية نظام التشغيل

هذا الفرق كبير جدّاً في مسرح الحوادث.

3.2. «حتّى ذلك، إن كان المستخدم نفسه يمكن فكّ التشفير؟» صحيح

هذه نقطة ينبغي كتابتها دون مراوغة.

الشيفرة التي تعمل بصلاحيّات المستخدم نفسه تستطيع في الأساس فكّ تشفير ما يستطيع ذلك المستخدم فكّ تشفيره.

أي أنّ DPAPI لا يستهدف أساساً حالات كهذه.

  • الجهاز مخترَق أصلاً ببرمجيّة خبيثة
  • المهاجم يستطيع تنفيذ شيفرة بصفة ذلك المستخدم
  • الاستيلاء كامل على مستوى مسؤول الجهاز

في هذه الحالة يستطيع التطبيق نفسه فكّ التشفير، فتستطيع شيفرة المهاجم ذلك أيضاً. هنا لا يُعتَمد كثيراً على «لكنّه مشفَّر».

ما ينفع فيه DPAPI أساساً هو جانب «تسرّب الملفّات وسوء الوضع والنقل دون اتصال والقراءة من مستخدم آخر».

إن خُلط هذا:

  • قُلِّل تقدير ما يُحمى فلم يُستخدم
  • أو بولغ في تقدير ما لا يُحمى فاطمأنّ الناس

كلاهما خطر بهدوء.

الجانب الذي ينفع فيه DPAPI والجانب الذي لا ينفعمخطّط يبيّن الخطّ الفاصل: DPAPI ينفع في تسرّب الملفّات وسوء الوضع والنقل دون اتصال والقراءة من مستخدم آخر، ولا ينفع ضدّ شيفرة هجوم تعمل بصلاحيّات المستخدم نفسه أو جهاز مخترَق.جانب ينفعجانب لا ينفعنطاق دفاع DPAPIالتسرّب وسوء الوضع ومستخدم آخرشيفرة بصلاحيّات المستخدم نفسهالخلط يُخطئ التقييم

الشكل 4: إن جُهل خطّ ما ينفع وما لا ينفع، صار التقييم إمّا تقليلاً فلا يُستخدم أو مبالغة في الاطمئنان.

3.3. إذن ما المفيد

إن قيلت فائدة DPAPI في جملة:

«يمكن فصل السرّ نفسه عن قابليّة قراءة ملفّ الإعداد»

مثلاً، في حوادث كهذه يظهر فرق بين النصّ الصريح و DPAPI.

  • أرسل المستخدم ملفّ الإعداد إلى الدعم
  • دخل ملفّ الإعداد أرشيف تحقيق
  • تسرّب ملفّ الإعداد وحده من نسخة احتياطيّة
  • نُسخ على مجلّد مشترك
  • أمكن جعل المطوّر يرى النصّ المشفَّر وحده دون قراءة المحتوى

هذه فائدة واقعيّة جدّاً. يمكن تصغير نصف قطر الحوادث اليوميّة دون جعل المهاجم بطلاً سينمائيّاً خارقاً.

آليّة تصغير نصف قطر الحادثمخطّط يبيّن أنّ الملفّ إن خرج في حوادث يوميّة مثل الإرسال الخطأ أو أرشيف التحقيق أو تسرّب النسخة الاحتياطيّة، فما يخرج نصّ مشفَّر فقط فلا يصير تسرّب سرّ، فيصغر نصف قطر الحادث اليوميّ.إرسال خطأ وأرشيف تحقيق وتسرّب نسخة احتياطيّةما يخرج نصّ مشفَّر فقطلا يصير تسرّب سرّ كما هويصغر نصف قطر الحادث اليوميّ

الشكل 5: حتّى إن تعذّر منع خروج الملفّ، يمكن تحويل ما يخرج إلى نصّ مشفَّر.

4. لماذا DPAPI مناسب تماماً

عند التعامل مع سرّ محفوظ محلّيّاً على Windows، أسباب مناسبة DPAPI للعمل كالتالي.

4.1. يمكن تقريب إدارة المفاتيح إلى نظام التشغيل

توليد مفتاح AES بنفسك، وحفظه، وإعطاء صلاحيّات، وتدويره، وتقدير أثر التسرّب، وإدخال كشف التلاعب أيضاً. هذا أثقل ممّا يُظَنّ. وإن عُمل باستخفاف انتهى الأمر عادةً بوضع المفتاح في نفس المكان.

باستخدام DPAPI يمكن فصل مشكلة «كيف يُصنَع مفتاح التشفير وأين يُوضَع» عن تنفيذ التطبيق.

بهذا المعنى، أقرب إلى الجوهر أن يُرى DPAPI لا «واجهة اختيار خوارزميّة التشفير» بل «واجهة تفويض إدارة المفاتيح إلى نظام التشغيل».

النظرة الجوهريّة إلى DPAPIمخطّط يبيّن أنّ DPAPI ليس واجهة اختيار خوارزميّة التشفير، بل أقرب إلى الجوهر أن يُرى واجهة تفويض مشكلة كيف يُصنَع المفتاح وأين يُوضَع إلى نظام التشغيل.ليست هذه النظرةهذه النظرة أقرب إلى الجوهرواجهة اختيار خوارزميّةDPAPIواجهة تفويض إدارة المفاتيح إلى نظام التشغيليمكن فصل توليد المفتاح وحفظه عن التنفيذ

الشكل 6: إن رُئي DPAPI جهة تفويض لإدارة المفاتيح، اتّضح ماذا يُفصَل عن التطبيق.

4.2. يمكن ربط جهة فكّ التشفير بمستخدم Windows أو بالحاسوب

في تطبيقات سطح المكتب العاديّة، كثيراً ما يكفي اختيار CurrentUser.

يمكن فكّ التشفير بفرضيّة:

  • أن يكون ذلك المستخدم مسجّل الدخول
  • أن تعمل المعالجة في سياق ذلك المستخدم

لذلك تُكتسَب خاصّيّة أنّ نسخ النصّ المشفَّر وحده إلى حاسوب آخر لا يسهّل استخدامه كما هو.

4.3. يسهل إدراج كشف التلاعب أيضاً

الشائع في التشفير الذاتيّ: اعتبار الأمر منتهياً بعد «التشفير بـ AES» ونسيان كشف التلاعب.

DPAPI يحمل أيضاً حماية سلامة لبيانات التشفير، فثمّة فائدة عمليّة: يسهل وضع كشف إعادة كتابة النصّ المشفَّر على آليّة جهة نظام التشغيل.

فرق كشف التلاعبمخطّط يبيّن أنّ التشفير الذاتيّ ينسى كشف التلاعب غالباً بعد اعتبار الأمر منتهياً بالتشفير بـ AES، بينما يحمل DPAPI حماية سلامة لبيانات التشفير فيسهل وضع كشف إعادة الكتابة على آليّة جهة نظام التشغيل.تشفير ذاتيّيسهل نسيان كشف التلاعبDPAPIيحمل حماية سلامةيركب كشف إعادة الكتابة أيضاً على جهة نظام التشغيل

الشكل 7: فائدة DPAPI أنّه يضع على آليّة نظام التشغيل لا التشفير وحده بل كشف التلاعب أيضاً.

4.4. يُستخدم مباشرة من C# / .NET

في C# يمكن استخدام System.Security.Cryptography.ProtectedData كما هو. الاستغناء عن مكتبات زائدة عون كبير أيضاً في تطبيقات حصريّة لـ Windows.

5. ما يحميه DPAPI وما لا يحميه

أأمن فصل هذا بوضوح.

5.1. ما يصير أسهل حماية

DPAPI نافذ على الأقلّ في مواضع كهذه.

  • تسرّب النصّ الصريح من ملفّ الإعداد
  • نقل الملفّ إلى حاسوب آخر
  • القراءة من مستخدم آخر على نفس الحاسوب (بفرضيّة CurrentUser)
  • التسرّب كنسخة احتياطيّة أو ملفّ مرفق
  • حالة «يُقرأ سهواً» في مواقع التطوير والصيانة

5.2. ما لا يُحمى، أو تكون الحماية فيه ضعيفة

بالمقابل، لا تبالغ في الاعتماد في الحالات التالية.

  • شيفرة هجوم تعمل بصلاحيّات المستخدم نفسه
  • اختراق الجهاز نفسه بالكامل
  • الاستيلاء بصلاحيّات المسؤول
  • النصّ الصريح في الذاكرة بعد أن يفكّ التطبيق التشفير
  • سرّ طويل العمر يُوزَّع مشتركاً على كلّ العملاء

الأخير، «السرّ الطويل العمر المشترك لكلّ العملاء»، مهمّ خصوصاً.

مثلاً، تصاميم مثل:

  • تضمين نفس مفتاح API لكلّ العملاء
  • حمل نفس كلمة المرور المشتركة على كلّ الأجهزة
  • توزيع مفتاح فكّ تشفير ثابت يكتمل في العميل وحده

يسهل امتدادها إلى الكلّ لحظة سحبها من جهاز واحد. المنطق بسيط: إن أمكن للتطبيق فكّ التشفير على جهاز واحد، أمكن استخراج ذلك السرّ.

DPAPI نافذ في «جعل موضع الحفظ ذلك أفضل من النصّ الصريح»، لكنّه لا يبرّر سرّاً لا ينبغي وضعه في العميل أصلاً.

آليّة امتداد السرّ الطويل العمر المشتركمخطّط يبيّن أنّ توزيع سرّ طويل العمر مشترك على كلّ العملاء يعني أنّ التطبيق يستطيع فكّ التشفير على جهاز واحد فيمكن استخراج السرّ، فيسهل الامتداد إلى الكلّ لحظة السحب من جهاز واحد.سرّ طويل العمر مشترك لكلّ العملاءالتطبيق يستطيع فكّ التشفير على جهاز واحديمكن استخراج السرّ من ذلك الجهازيمتدّ إلى الكلّالحفظ بـ DPAPI لا يحلّ الأصل

الشكل 8: السرّ المشترك يمتدّ إلى الكلّ بسقوط جهاز واحد، فراجِع موضع الوضع لا أسلوب الحفظ.

أصل هذا النوع من الأسرار ليس ابتكار أسلوب الحفظ، بل تهريبه في الاتّجاهات التالية.

  • وضعه في جهة الخادم
  • أن يحمل العميل رمزاً فقط
  • جعله معلومات اعتماد لكلّ مستخدم
  • جعله رمزاً محدود الأجل

6. التفريق بين CurrentUser و LocalMachine

هذه نقطة مهمّة جدّاً. الاختيار باستخفاف يغيّر المعنى.

6.1. الأساس CurrentUser

في تطبيقات سطح مكتب Windows العاديّة فكّر أوّلاً بـ CurrentUser أساساً.

أمثلة مناسبة:

  • تطبيقات سطح مكتب موجَّهة للمستخدم بـ WPF / WinForms / WinUI
  • تطبيقات تحمل إعداداً أو معلومات اعتماد لكلّ مستخدم
  • تطبيقات تحمل الإعداد تحت %LocalAppData% أو %AppData%

عندئذ يسهل معاملته «سرّ مستخدم Windows ذلك».

6.2. استخدامات LocalMachine ضيّقة جدّاً

LocalMachine يبدو مريحاً، لكنّه أوسع ممّا يلزم في تطبيقات سطح المكتب العاديّة.

يناسبه مثلاً حالات كهذه.

  • خدمة Windows على جهاز موثوق لغرض واحد
  • سرّ تستخدمه عمليّة معيّنة فقط على ذلك الجهاز
  • حالة تحتاج الاستخدام على نفس الجهاز عبر مستخدمي تسجيل الدخول

لكن التنبيهات ثقيلة.

  • يمكن فكّ التشفير على نطاق واسع من العمليّات العاملة على ذلك الحاسوب
  • يسهل أن يصير خطراً على أجهزة مشتركة و RDS وأجهزة وسيطة وبيئات يدخلها أكثر من مستخدم
  • إن اختير لأنّ «الجميع يستطيع الاستخدام فهو أسهل»، ضاق الأمر لاحقاً في الغالب

ودوافع الرغبة في اختيار LocalMachine عادةً هذه الثلاث.

  • يمكن القراءة بعد تبديل المستخدم
  • يمكن القراءة من الخدمة أيضاً
  • مريح إن عمل

كلّها «سهولة»، وليست «محميّ». إن اخترتَ LocalMachine في تطبيق سطح مكتب عاديّ، وسّعتَ إمكان فكّ التشفير إلى عمليّات أخرى على ذلك الحاسوب، فيتغيّر المعنى كثيراً.

ماذا يحدث إن اختير LocalMachine للسهولةمخطّط يبيّن أنّ اختيار LocalMachine لسهولة مثل القراءة عبر المستخدمين أو من الخدمة أيضاً يوسّع إمكان فكّ التشفير إلى عمليّات أخرى على ذلك الحاسوب، فيصير الأمر سهولة لا حماية.سهولة عبر المستخدمين ودعم الخدمةاختيار LocalMachineيتّسع إمكان فكّ التشفير إلى عمليّات أخرى على الحاسوبسهولة وليس حماية

الشكل 9: إن اختير LocalMachine للسهولة، اتّسع نطاق من يستطيع فكّ التشفير إلى الحاسوب كلّه.

6.3. عند التردّد فكّر هكذا

  • تطبيق واجهة عاديّ -> CurrentUser
  • حالة خاصّة تريد الحماية حقّاً بوحدة الجهاز -> LocalMachine
  • يلزم أن يستطيع أيّ مستخدم فكّ التشفير، لكن على الجهاز مستخدمون آخرون أيضاً -> الأولى في الغالب مراجعة التصميم من أصله

6.4. مع الخدمات و impersonation يزداد الانتباه قليلاً

إن دخلت خدمة Windows أو impersonation، ثقل معنى CurrentUser قليلاً.

  • من هو حساب التنفيذ
  • هل حُمِّل الملفّ الشخصيّ لذلك الحساب
  • في أيّ سياق لحظة فكّ التشفير

إن انزاح هذا المحيط، سهل أن يصير الأمر «نجح التشفير وتعذّر فكّه». استخدامات الخدمة قد لا تُنهى بـ «CurrentUser مؤقّتاً».

مع impersonation، الفشل النموذجيّ المذكور أيضاً في Microsoft Learn هو «Key not valid for use in specified state.». DPAPI يحفظ بيانات المفتاح في الملفّ الشخصيّ للمستخدم، فلا يمكن فكّ التشفير إن لم يُحمَّل الملفّ الشخصيّ. يلزم تحميل الملفّ الشخصيّ للمستخدم المستهدَف قبل الـ impersonate.

فشل نموذجيّ مع impersonationمخطّط يبيّن أنّ DPAPI يحفظ بيانات المفتاح في الملفّ الشخصيّ للمستخدم، لذا إن فُكّ التشفير بعد impersonate دون تحميل الملفّ الشخصيّ حدث خطأ نموذجيّ، ويلزم تحميل الملفّ الشخصيّ للمستخدَم المستهدَف أوّلاً.impersonate دون تحميليفشل فكّ التشفير بخطأتحميل الملفّ الشخصيّ أوّلاًيمكن فكّ التشفير بعد impersonate أيضاًالمفتاح في الملفّ الشخصيّ

الشكل 10: المفتاح في جهة الملفّ الشخصيّ، لذا يلزم تحميل الملفّ الشخصيّ قبل impersonate.

6.5. اعرف حالات «يتعذّر فكّ التشفير» في التشغيل

في العمل، حادث تعذّر فكّ التشفير أوجع من التشفير نفسه. DPAPI آليّة تربط جهة فكّ التشفير بمستخدم Windows / بالحاسوب، فإن انقطع ذلك الربط تعذّرت القراءة.

ما تريد معرفته مسبقاً هذه الخمس.

الحالة ماذا يحدث كيف تستعدّ
إعادة تعيين كلمة المرور من المسؤول تنفكّ الحماية المرتبطة بكلمة مرور المستخدم، وقد يتعذّر الوصول إلى البيانات المحميّة بـ DPAPI. معلومات دعم Microsoft تسجّل أيضاً حالة تعذّر الوصول إلى بيانات DPAPI بعد إعادة تعيين المسؤول لكلمة المرور صمّم السرّ بحيث «يمكن إعادة الحصول عليه». وجّه إلى إعادة الإدخال عند فشل فكّ التشفير
إعادة إنشاء الملفّ الشخصيّ الملفّ الشخصيّ الجديد يحمل مادّة مفتاح أخرى، فلا يمكن فكّ تشفير النصّ المشفَّر السابق احمل إصداراً لملفّ الإعداد، ولا تجعل فشل فكّ التشفير إنهاءً غير طبيعيّ
نسخ النصّ المشفَّر وحده إلى حاسوب آخر مادّة المفتاح اللازمة لفكّ التشفير في جهة الملفّ الشخصيّ للمستخدم، فنقل نصّ CurrentUser المشفَّر وحده لا يكفي للقراءة (هذا الوجه الآخر لـ«القوّة» المذكورة في 3.1) صمّم بفرضيّة إعادة الحفظ لكلّ جهاز
الملفّ الشخصيّ الجوّال هنا يمكن القراءة. لأنّ مادّة المفتاح تنتقل مع الملفّ الشخصيّ، تنصّ Microsoft Learn أيضاً على أنّ «المستخدم ذا الملفّ الشخصيّ الجوّال يستطيع فكّ التشفير من حاسوب آخر على الشبكة». إن عومل معاملة الصفّ السابق، أُعيد إنشاء معلومات الاعتماد بلا داعٍ في إجراء الانتقال لا تحسم أنّ «حاسوباً آخر يعني تعذّر القراءة». أكّد وجود التجوال ثمّ قرّر إجراء الانتقال
تغيير حساب تنفيذ الخدمة إن اختلف حساب التنفيذ بين الحماية وفكّ التشفير، تعذّرت القراءة بـ CurrentUser أدخل في التشغيل إجراء إعادة حماية عند تغيير الحساب

الخلاصة: اكتب الشيفرة بفرضيّة أنّ ProtectedData.Unprotect قد يفشل. عند فشل فكّ التشفير تُرمى CryptographicException، فأمسكها ووجّه إلى إعادة الإدخال.

using System;
using System.Security.Cryptography;
using System.Text;

// protectedBase64: النص المشفّر المقروء من ملف الإعداد (Base64)
// entropy: مرِّر القيمة نفسها المستخدمة عند الحماية. null جائز
static bool TryUnprotect(string protectedBase64, byte[]? entropy, out string plaintext)
{
    plaintext = string.Empty;

    try
    {
        byte[] plainBytes = ProtectedData.Unprotect(
            Convert.FromBase64String(protectedBase64),
            optionalEntropy: entropy,
            scope: DataProtectionScope.CurrentUser);

        plaintext = Encoding.UTF8.GetString(plainBytes);
        return true;
    }
    catch (CryptographicException)
    {
        // تعذّر فك التشفير = يرجّح أن البيئة تغيّرت.
        // لا تُسقط هنا؛ وجِّه المستدعي إلى مسار إعادة الإدخال
        return false;
    }
    catch (FormatException)
    {
        // عندما تكون سلسلة Base64 تالفة
        return false;
    }
}

الاستعلامات التي تأتي بـ «شفّرنا وتعذّر فكّ التشفير» تكون في الغالب واحدة من صفوف هذا الجدول.

تدفّق الشيفرة بفرضيّة فشل فكّ التشفيرمخطّط يبيّن كتابة الشيفرة بفرضيّة أنّ ProtectedData.Unprotect قد يفشل لتغيّر البيئة، وإمساك CryptographicException دون إنهاء غير طبيعيّ، والتوجيه إلى مسار إعادة الإدخال.نجاحفشل باستثناءمحاولة Unprotectاستخدام السرّإمساك الاستثناء دون الإسقاطالتوجيه إلى مسار إعادة الإدخالقد يفشل إن انقطع الربط

الشكل 11: اكتب بفرضيّة أنّ فكّ التشفير قد يفشل، وصِل الفشل بإعادة الإدخال لا بإنهاء غير طبيعيّ.

7. حدّ أدنى من إرشادات التنفيذ

إن كان المراد في تطبيق Windows «إيقاف النصّ الصريح في ملفّ الإعداد» فقط، فلا حاجة لتعقيد التصميم أكثر من اللازم. لكن ثمّة نقاط لا تريد تجاوزها.

7.1. احمِ الأسرار وحدها

أسهل معاملة أن تحمي بنود السرّ وحدها أوّلاً، لا تشفير ملفّ الإعداد كلّه.

مثلاً، افصل كالتالي.

  • عنوان URL للخادم
  • اسم المستخدم
  • اسم قاعدة البيانات
  • أعلام الوظائف

كثيراً ما يكفي إبقاؤها نصّاً صريحاً.

بالمقابل،

  • كلمة المرور
  • رمز API
  • رمز التحديث (refresh token)
  • معلومات اعتماد مجلّد مشترك

أهداف حماية.

بهذا الفصل يصير:

  • تحرير الإعداد أسهل
  • تأكيد الفروق أسهل
  • موضع السرّ واضحاً
  • التشغيل كلّه أبسط
فصل حماية بنود السرّ وحدهامخطّط يبيّن أنّ حماية بنود السرّ وحدها مثل كلمة المرور والرموز، مع إبقاء URL واسم المستخدم ونحوهما نصّاً صريحاً بدل تشفير الإعداد كلّه، تسهّل التحرير وتوضّح موضع السرّ.يبقى نصّاً صريحاًيُحمىملفّ الإعدادURL واسم المستخدم والأعلام وغيرهاكلمة المرور والرموز وغيرهاموضع السرّ واضح والتشغيل أبسط

الشكل 12: حماية بنود السرّ وحدها لا التشفير الكلّيّ تجمع سهولة المعاملة والأمن.

7.2. جهة الحفظ أساسها per-user

في تطبيقات سطح المكتب العاديّة اجعل جهة الحفظ أساساً موضعاً per-user.

  • %LocalAppData%\Vendor\App\settings.json
  • %AppData%\Vendor\App\settings.json

على الأقلّ، لا تضعه باستخفاف تحت مجلّد التثبيت أو في موضع سهل المشاركة.

حتّى مع حماية DPAPI، إن كانت ACL جهة الحفظ مستخفّة، صار الحديث: «يُقرأ النصّ المشفَّر» و«تُرى بنية الإعداد» و«تحدث أخطاء تشغيل». الدفاع أنجع حين يُرصَف طبقات لا طبقة واحدة.

7.3. optionalEntropy ليس مفتاحاً ثانياً سحريّاً لكلّ شيء

يمكن تمرير optionalEntropy إلى ProtectedData. هذا مريح، لكنّه ليس «مفتاحاً ثانياً سحريّاً يصير آمناً إن دُفن في الثنائيّ».

  • إن وُضع في نفس الملفّ لم يصر سرّاً
  • دفنه قيمة ثابتة في الثنائيّ لا يجعله سرّاً قويّاً
  • ومع ذلك ينفع في تمييز الغرض ومنع إساءة الاستخدام

عمليّاً، المناسب تقريباً تمرير

  • اسم التطبيق
  • اسم الغرض
  • معرّف الإصدار

بايتات ثابتة، واستخدامه من أجل «عدم قبول نصّ مشفَّر لغرض آخر خطأ».

الموضع الصحيح لـ optionalEntropyمخطّط يبيّن أنّ optionalEntropy ليس مفتاحاً ثانياً سحريّاً يصير آمناً إن دُفن في الثنائيّ، بل المناسب استخدامه لتمييز الغرض بتمرير اسم التطبيق واسم الغرض بايتات ثابتة كي لا يُقبَل نصّ مشفَّر لغرض آخر خطأ.لا تنتظر هذاهذا الاستخدام مناسبoptionalEntropyمفتاح ثانٍ سحريّمعرّف اسم التطبيق واسم الغرضعدم قبول نصّ مشفَّر لغرض آخر خطأ

الشكل 13: entropy ليس مفتاحاً سرّيّاً؛ يُستخدم وسماً لتمييز الغرض ومنع إساءة الاستخدام.

7.4. ليس مسموحاً إدخال النصّ المشفَّر إلى Git

هذه أيضاً مهمّة بهدوء.

نصّ DPAPI المشفَّر أفضل بكثير من النصّ الصريح، لكن ذلك لا يعني جواز إدخال ملفّ الإعداد كلّه إلى المستودع.

السبب بسيط:

  • النصّ المشفَّر يبقى طويلاً
  • قد يُعاد إنتاج نفس الجهاز أو نفس السياق يوماً ما
  • الملفّ يحمل معلومات غير السرّ أيضاً
  • تنشأ ثقافة «محميّ إذن يمكن التعامل معه باستخفاف»

«أفضل من النصّ الصريح» و «آمن أينما وُضع» أمران مختلفان تماماً.

سبب عدم إدخال النصّ المشفَّر إلى Git أيضاًمخطّط يبيّن أنّ نصّ DPAPI المشفَّر أفضل من النصّ الصريح، لكن إدخاله المستودع يبقيه طويلاً ويشمل معلومات غير السرّ وينشئ ثقافة تعامل مستخفّ لأنّه محميّ، فهو غير «آمن أينما وُضع».أفضل من النصّ الصريحومع ذلكنصّ DPAPI المشفَّريصير أقوى أمام الحوادثلا يُدخَل المستودعيبقى طويلاً وتدخل معلومات أخرى وتلين الثقافة

الشكل 14: «أفضل من النصّ الصريح» ليس سبباً لاستخفاف موضع الوضع.

7.5. لا تخرجه إلى السجلّ

نمط شائع على غير توقّع: فكّ التشفير ثمّ الإخراج إلى السجلّ فيفسد الكلّ.

  • إخراج سلسلة الاتّصال كاملة عند فشل الاتّصال
  • إبقاء ترويسة Authorization عند API 401
  • خلط السرّ في رسالة الاستثناء

إن فُعل هذا، حتّى بعد إيقاف النصّ الصريح في ملفّ الإعداد، صار السجلّ مستودع نصّ صريح. محزن، لكنّه عمل كثير الوقوع.

8. مثال تنفيذ أدنى بـ C# / .NET

8.1. أوّلاً، يلزم إضافة مرجع

ProtectedData يبدو داخلاً في BCL، لكن «من أين يأتي» يختلف حسب إطار الهدف. إن تعثّرت هنا، تعذّر حلّ اسم النوع ProtectedData.

الهدف العمل اللازم جهة التوفير
.NET Framework إضافة مرجع تجميعة System.Security إلى المشروع System.Security.dll
.NET Core / .NET 5 فما فوق (بما في ذلك .NET 6 / 8) إضافة حزمة NuGet System.Security.Cryptography.ProtectedData System.Security.Cryptography.ProtectedData.dll

هذه الحزمة غير مضمَّنة في أيّ إطار عمل مشترك لـ .NET Core / .NET 5 فما فوق. حتّى مع هدف موجَّه إلى Windows مثل net8.0-windows يلزم مرجع صريح.

dotnet add package System.Security.Cryptography.ProtectedData

ونقطة أخرى تريد معرفتها قبل التنفيذ. ProtectedData حصريّ لـ Windows. لأنّه يعتمد على DPAPI، فإن استُدعي على .NET لمنصّة غير Windows رُميت PlatformNotSupportedException. إن كان أساس الشيفرة بفرضيّة عابر المنصّات، فصمّم تصميماً آخر من البداية كما في 10.1.

ما يُؤكَّد قبل استخدام ProtectedDataمخطّط يبيّن نقطتين ينبغي تأكيدهما قبل التنفيذ: ProtectedData يحتاج إضافة مرجع حسب الهدف وإلا تعذّر حلّ اسم النوع، وهو حصريّ لـ Windows فإن استُدعي خارج Windows صار استثناء وقت التشغيل.إن لم تُضَفإن استُدعي خارج Windowsنريد استخدام ProtectedDataإضافة مرجع حسب الهدفيتعذّر حلّ اسم النوعفهم أنّه حصريّ لـ Windowsيصير استثناء وقت التشغيل

الشكل 15: قبل التنفيذ أمسك فرضيّتين: إضافة المرجع، والحصرية لـ Windows.

8.2. تنفيذ أدنى

التالي مثال أدنى لحماية سلسلة تُحفَظ في ملفّ الإعداد بـ CurrentUser. أدخلنا optionalEntropy ثابتاً لتمييز الغرض، لكن لا تحسبه مفتاحاً سرّيّاً.

using System;
using System.Security.Cryptography;
using System.Text;

public static class DpapiSecretProtector
{
    // 用途識別用。第二の秘密鍵ではない。
    private static readonly byte[] Entropy =
        Encoding.UTF8.GetBytes("ComComponent:DesktopApp:SettingsSecret:v1");

    public static string ProtectToBase64(string plaintext)
    {
        ArgumentNullException.ThrowIfNull(plaintext);

        byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext);
        byte[] protectedBytes = Array.Empty<byte>();

        try
        {
            protectedBytes = ProtectedData.Protect(
                plainBytes,
                optionalEntropy: Entropy,
                scope: DataProtectionScope.CurrentUser);

            return Convert.ToBase64String(protectedBytes);
        }
        finally
        {
            Array.Clear(plainBytes, 0, plainBytes.Length);

            if (protectedBytes.Length > 0)
            {
                Array.Clear(protectedBytes, 0, protectedBytes.Length);
            }
        }
    }

    public static string UnprotectFromBase64(string protectedBase64)
    {
        ArgumentNullException.ThrowIfNull(protectedBase64);

        byte[] protectedBytes = Convert.FromBase64String(protectedBase64);
        byte[] plainBytes = Array.Empty<byte>();

        try
        {
            plainBytes = ProtectedData.Unprotect(
                protectedBytes,
                optionalEntropy: Entropy,
                scope: DataProtectionScope.CurrentUser);

            return Encoding.UTF8.GetString(plainBytes);
        }
        finally
        {
            Array.Clear(protectedBytes, 0, protectedBytes.Length);

            if (plainBytes.Length > 0)
            {
                Array.Clear(plainBytes, 0, plainBytes.Length);
            }
        }
    }
}

الاستخدام بسيط.

string protectedPassword = DpapiSecretProtector.ProtectToBase64(password);

// JSON などへ保存
// settings.DbPasswordProtected = protectedPassword;

string password = DpapiSecretProtector.UnprotectFromBase64(settings.DbPasswordProtected);

ملفّ الإعداد يمكن أن يصير مثلاً بهذا الشكل.

{
  "ApiBaseUrl": "https://api.example.com/",
  "UserName": "app-user",
  "PasswordProtected": "AQAAANCMnd8BFdERjHoAwE..."
}

حسن هذا الشكل:

  • يمكن تحرير URL واسم المستخدم كالعادة
  • يمكن حماية كلمة المرور وحدها
  • بنية الإعداد واضحة
  • أقلّ عرضة للحوادث من الوضع نصّاً صريحاً

9. تصاميم ما زالت خطرة رغم ذلك

حتّى مع استخدام DPAPI، التصاميم التالية ما زالت خطرة.

9.1. تدوير القيمة طويلاً بعد فكّ التشفير

ما ينبغي تجنّبه إخراج القيمة بعد فكّ التشفير إلى:

  • السجلّ
  • الشاشة
  • الاستثناء
  • كائن طويل العمر وتركها عليه

«التشفير عند الحفظ» و «آمن أيضاً أثناء الاستخدام» مشكلتان منفصلتان.

9.2. إعطاء كلّ التثبيتات سرّاً مشتركاً

تصميم إعطاء كلّ المستخدمين نفس مفتاح API لا يحلّ الأصل حتّى إن حُفظ بـ DPAPI. السبب وإلى أين يُهرَّب بدل ذلك ملخَّص في 5.2.

9.3. اختيار LocalMachine لأنّه «أسهل»

هذا أيضاً شائع حقّاً. لكنّه «سهولة» لا «محميّ». دوافع الرغبة في الاختيار وما يتّسع عندئذ كما في 6.2. إن تردّدت فانظر أسطر 6.3 الثلاثة.

9.4. الاطمئنان بإضافة تشفير ذاتيّ

إدخال تنفيذ بدل DPAPI مثل:

  • دفن مفتاح AES في الشيفرة المصدريّة
  • وضع مفتاح AES في بند آخر من ملفّ الإعداد
  • معاملة «سلسلة غُمِّضت قليلاً» معاملة مفتاح

غالباً ضعيف الأثر.

بين «ليس نصّاً صريحاً» و «آمن» هوّة كبيرة.

خطر الاطمئنان بالتشفير الذاتيّمخطّط يبيّن أنّ التشفير الذاتيّ الذي يدفن مفتاح AES في الشيفرة أو في بند آخر من ملفّ الإعداد أو يعامل سلسلة غُمِّضت معاملة مفتاح ضعيف الأثر غالباً، وأنّ بين «ليس نصّاً صريحاً» و «آمن» هوّة كبيرة.هوّة كبيرة بينهماتشفير ذاتيّ يدفن المفتاح في الشيفرة أو الإعدادحالة ليست نصّاً صريحاًحالة آمنةالأثر ضعيف غالباً

الشكل 16: التشفير الذاتيّ الذي يضع المفتاح في نفس المكان يصل إلى «ليس نصّاً صريحاً» فقط، ولا يبلغ «آمناً».

10. حالات لا يكفي فيها DPAPI

DPAPI مريح لكنّه ليس لكلّ شيء. في الحالات التالية أولى التفكير في خيار آخر.

10.1. تريد التشغيل خارج Windows أيضاً

DPAPI / ProtectedData موجَّهان إلى Windows. لا يمكن البناء على هذه الفرضيّة في تطبيق عابر المنصّات.

10.2. تريد معاملة نفس السرّ عبر أجهزة أو مستخدمين متعدّدين

متطلّب فكّ تشفير نفس النصّ المشفَّر على عدّة حواسيب، أو مشاركته بين عدّة مستخدمين، يخرج عن مجال براعة DPAPI في «الربط بذلك الجهاز وبذلك المستخدم».

عندئذ ينبغي التفكير في تصميم آخر يوافق المتطلّب، مثل:

  • إدارة الأسرار في جهة الخادم
  • أساس معلومات الاعتماد
  • مصادقة Windows / المصادقة المتكاملة
  • مخزن معلومات اعتماد للتطبيق

10.3. هدف الحفظ هو معلومات اعتماد المستخدم نفسها

إن كان ما تريد حفظه بوضوح زوجاً من:

  • اسم المستخدم
  • كلمة المرور

فأوضح من الكتابة إلى ملفّك بـ DPAPI مخزن معلومات الاعتماد الذي يوفّره Windows. هذا موضع تردّد شائع في العمل، لذا نضع مقارنة.

الزاوية DPAPI (ProtectedData) Credential Locker (PasswordVault) Credential Manager (CredWrite / CredRead)
موضع الحفظ ملفّ تقرّره أنت (كيف يُوضَع النصّ المشفَّر شأن التطبيق) مخزن معلومات اعتماد يديره Windows مخزن معلومات اعتماد يديره Windows
ما يمكن حفظه أيّ تسلسل بايتات (سلسلة اتّصال، رمز، جزء من الإعداد أيضاً) زوج اسم مستخدم + كلمة مرور معلومات اعتماد (بنية حسب النوع)
الواجهة System.Security.Cryptography WinRT Windows.Security.Credentials Win32 (wincred.h / Advapi32.dll)
هل يُستخدم من تطبيق سطح المكتب يُستخدم كما هو يُستخدم لا من WinUI وحده بل من WPF / WinForms أيضاً (يلزم إعداد استدعاء WinRT API) يُستخدم كما هو
المزامنة لا تجوال بين الأجهزة بحساب Microsoft لا (مجموعة معلومات اعتماد المستخدم المحلّيّة)
القيود لا قيود تُذكَر عمليّاً حتّى 20 عنصراً لكلّ تطبيق. ليس للبيانات الكبيرة يرتبط بجلسة تسجيل دخول الرمز الحالي
جهة الإدارة التطبيق (تقرّر جهة الحفظ و ACL أيضاً) نظام التشغيل (لا تصمّم موضع الحفظ) نظام التشغيل (يمكن الإدارة من مدير معلومات الاعتماد في لوحة التحكّم)

مؤشّر التفريق كالتالي.

  • هدف الحفظ زوج اسم مستخدم + كلمة مرور والعدد قليل -> المرشّح الأوّل Credential Locker / Credential Manager. تستغني عن تصميم موضع الحفظ وعن حمل ACL بنفسك
  • هدف الحفظ ليس شكل «اسم مستخدم + كلمة مرور» -> سلسلة اتّصال، رمز API، رمز تحديث، جزء من ملفّ الإعداد أوضح في جهة DPAPI. وهذا ما يعالجه المقال
  • تريد التوريث بين الأجهزة -> تجوال Credential Locker ينفع. CurrentUser في DPAPI بالعكس ميزته «لا يُورَّث»، فالغرض هنا عكس ذلك
  • العدد كبير / الحجم كبير -> تصطدم بحدّ 20 عنصراً في Credential Locker. قرّب إلى ملفّ ذاتيّ بـ DPAPI

ومخزن معلومات الاعتماد أيضاً يركب آليّة حماية نظام التشغيل، فلا ترتيب من نوع «أأمن من DPAPI» أو «DPAPI أدنى». العمليّ الاختيار حسب شكل ما تريد حفظه وحاجة التجوال.

وأيّاً اخترت، يستحقّ في تطبيق جديد النظر أوّلاً إلى بلا كلمة مرور مثل Windows Hello / مفاتيح المرور (passkeys). إن أمكن الاستغناء عن كلمة مرور طويلة العمر أصلاً، فذلك الأقوى.

مركز هذا المقال، على أيّ حال، خطّ DPAPI العمليّ من أجل «إيقاف النصّ الصريح في ملفّ إعداد عميل Windows».

11. ترتيب أولويّة موصى به في العمل

أخيراً، إن تردّدت في العمل يسهل الترتيب بهذا التسلسل. انظر من الأعلى بالترتيب، وانزل إلى الأسفل فقط حين لا تُستوفى الشروط.

يمكنلا يمكنلا يمكن الفصليمكن الفصلذلك الزوج والعدد قليلنعمنعمحساب مجال / محلّيّلاشكل آخر مثل الرموزنعملاحساب واحد يكفي (المستخدم نفسه،أو حساب خدمة مخصّص)يلزم فكّ التشفيرمن حسابات متعدّدةيمكن الجزملا يمكن الجزمهل يمكن الاستغناء عن حملسرّ طويل العمر على الجهازالأولويّة 1: لا نحمل(مصادقة Windows ورموز قصيرة العمر)هل يمكن فصل السرّلكلّ مستخدمراجِع التصميم من أصلهألّا يصير مفتاحاً مشتركاًشكل ما يُحفَظزوج اسم مستخدم + كلمة مرور؟ (القسم 10.3)هل يلزم التوريث بين الأجهزةأجهزة تُزامَنبحساب Microsoft؟ (القسم 10.3)استخدام تجوالCredential LockerDPAPI لا يورّث.الإدارة في جهة الخادم (القسم 10.2)Credential Locker /Credential Managerهل يلزم التوريث بين الأجهزةكم حساباً يفكّ تشفيرنفس السرّالأولويّة 3: DPAPI + CurrentUserإن كان التنفيذ بلا مراقبة فأكّدتحميل الملفّ الشخصيّ (القسم 6.4)هل يمكن الجزم بأنّمستخدمين آخرين لا يدخلونالأولويّة 4: DPAPI + LocalMachineاستثناء مع إبقاء الحجّةيمكن لمستخدم آخر فكّ التشفير أيضاً.راجِع أسلوب المصادقة

الشكل 17: ترتيب اختيار أسلوب الحفظ. انظر أوّلاً إلى شكل السرّ وحاجة التجوال ثمّ ادخل DPAPI. LocalMachine يُختار استثناء حين لا تقوم الخيارات الأخرى، لا لأنّه «أسهل»

الأولويّة 1: لا تحمل أصلاً

  • مصادقة Windows
  • المصادقة المتكاملة
  • تسجيل الدخول التفاعليّ
  • إبقاء السرّ في جهة الخادم
  • رموز قصيرة العمر

الأولويّة 2: قرّب إلى سرّ لكلّ مستخدم

  • per-user أفضل من سرّ مشترك
  • رمز قابل للتحديث أفضل من معلومات اعتماد ثابتة طويلة العمر
  • تجنّب مفتاحاً مشتركاً لكلّ العملاء

الأولويّة 3: إن لزم الحفظ المحلّيّ فـ DPAPI

  • عادةً CurrentUser
  • جهة الحفظ per-user
  • حماية بنود السرّ وحدها
  • لا تخرجه إلى السجلّ

الأولويّة 4: LocalMachine استثناء

  • هل يلزم حقّاً بوحدة الجهاز
  • هل لا يدخل ذلك الجهاز مستخدمون آخرون
  • هل سليم كتصميم خدمة

12. الخلاصة

حين يحتاج تطبيق Windows إلى حفظ معلومات سرّيّة في ملفّ إعداد، تريد تجنّب الوضع نصّاً صريحاً.

وعلى سؤال:

«على أيّ حال سيُحفَظ المفتاح في مكان ما، أليس الأمر سواء؟»

الجواب العمليّ كالتالي.

  • إن وضعتَ المفتاح في نفس المكان بتشفير ذاتيّ، فالأمر قريب جدّاً من السواء
  • DPAPI ليس سواء
    • يمكن تقريب إدارة المفاتيح إلى نظام التشغيل
    • يمكن ربط جهة فكّ التشفير بمستخدم Windows / بالحاسوب
    • يمكن ألّا يصير تسرّب الملفّ وحده تسرّب سرّ كما هو
  • لكنّه لا يحلّ
    • الشيفرة التي تعمل بصلاحيّات المستخدم نفسه
    • جهازاً مخترَقاً بالكامل
    • سرّاً مشتركاً طويل العمر لا ينبغي وضعه في العميل

الخلاصة: DPAPI ليس سوراً لكلّ شيء. لكن له أثر استبدال زجاج نافذة إعداد النصّ الصريح الشفّاف بنافذة مقبولة في الحدّ الأدنى.

في عمل عميل Windows هذا الفرق كبير جدّاً. أواقعيّ أن تبدأ من عدم تجاوز هذا الموضع.

موضع DPAPI الواقعيّمخطّط يبيّن أنّ DPAPI ليس سوراً لكلّ شيء، لكن له أثر استبدال زجاج نافذة إعداد النصّ الصريح الشفّاف بنافذة مقبولة في الحدّ الأدنى، وأنّ البدء من هناك واقعيّ.الاستبدال بـ DPAPIإعداد نصّ صريح = زجاج نافذة شفّافنافذة مقبولة في الحدّ الأدنىليس سوراً لكلّ شيءالبدء من هنا واقعيّ

الشكل 18: DPAPI ليس سوراً بل استبدال نافذة، لكن في العمل هذا الفرق هو الأكثر نفعاً.

13. روابط مرجعية

أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.

ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.

الأسئلة الشائعة

أسئلة شائعة حول موضوع هذه المقالة.

ما هو DPAPI؟
DPAPI (Data Protection API) آليّة لحماية البيانات يوفّرها Windows، تُفوَّض فيها إدارة مفاتيح التشفير إلى نظام التشغيل، وتُربَط جهة فكّ التشفير بمستخدم Windows ذلك أو بذلك الحاسوب. من C# / .NET تُستخدم عبر الصنف System.Security.Cryptography.ProtectedData دون إضافة مكتبات زائدة. أقرب إلى جوهرها أن تُرى واجهة تفويض إدارة المفاتيح إلى نظام التشغيل، لا واجهة اختيار خوارزميّة التشفير. وهي خيار واقعيّ لعدم ترك كلمات المرور ورموز API المحفوظة في ملفّ الإعداد بالنصّ الصريح.
المفتاح سيُحفَظ في مكان ما على أيّ حال، أليس النصّ الصريح و DPAPI سواء؟
ليسا سواء. إن وضعتَ مفتاح AES ذاتيّ في نفس التطبيق أو نفس ملفّ الإعداد فأنت قريب جدّاً من النصّ الصريح، أمّا DPAPI فيقرّب إدارة المفاتيح إلى نظام التشغيل ويربط جهة فكّ التشفير بمستخدم Windows أو بالحاسوب. لذلك تتغيّر القوّة كثيراً أمام حوادث مثل تسرّب ملفّ الإعداد وحده، أو نقله إلى حاسوب آخر، أو إرساله خطأ، أو تسرّب نسخة احتياطيّة، أو دخوله المستودع. الفرق الحاسم عن النصّ الصريح أنّ DPAPI يفصل «يمكن قراءة الملفّ» عن «يمكن استخدام السرّ».
ما الذي لا يحميه DPAPI؟
الشيفرة التي تعمل بصلاحيّات المستخدم نفسه تستطيع في الأساس فكّ تشفير ما يستطيع ذلك المستخدم فكّ تشفيره. لذلك لا يحمي من حالة اختراق الجهاز ببرمجيّة خبيثة، أو الاستيلاء بصلاحيّات المسؤول، أو النصّ الصريح في الذاكرة بعد فكّ التشفير. كذلك السرّ الطويل العمر الموزَّع مشتركاً على كلّ العملاء يسهل امتداده إلى الكلّ لحظة سحبه من جهاز واحد، فحفظه بـ DPAPI لا يحلّ الأصل. ما ينفع فيه DPAPI أساساً هو جانب تسرّب الملفّات وسوء الوضع والنقل دون اتصال والقراءة من مستخدم آخر.
أيّهما يُستخدم في DataProtectionScope: CurrentUser أم LocalMachine؟
في تطبيقات سطح مكتب Windows العاديّة الأساس CurrentUser. يُعامَل السرّ سرّ ذلك المستخدم، وتصير خاصّيّة أنّ نسخ النصّ المشفَّر وحده إلى حاسوب آخر لا يسهّل استخدامه كما هو. LocalMachine يمكن فكّ تشفيره على نطاق واسع من العمليّات على ذلك الحاسوب، فيسهل أن يصير خطراً على أجهزة مشتركة وبيئات يدخلها أكثر من مستخدم، وتضيق استخداماته كثيراً إلى خدمات Windows على جهاز موثوق لغرض واحد. إن اخترتَ LocalMachine لأنّ «الجميع يستطيع الاستخدام فهو أسهل»، فستضيق لاحقاً في الغالب.

الملف الشخصي للمؤلف

صفحة الملف الشخصي لمؤلف المقالة.

غو كومورا

مؤسّس شركة كومورا سوفت ذ.م.م.

يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.

العودة إلى المدونة