تصميم أمن التحديث التلقائي - لماذا لا يكفي HTTPS
· آخر تحديث: · 小村 豪 · تطوير Windows, الأمان, updater, التحديث التلقائي, التوقيع, MSIX, ClickOnce
سجل التعديلات (4 تحديثات، آخر تحديث 3 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُضيفت روابط الاستشارة الموجودة في الأصل الياباني (consultation_services). ولم يتغيّر نصّ المقالة نفسه. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22243182)
- أُصلِحت روابط الخلاصة وCTA الخدمة ورسالة رفض قاعدة البيانات الأحدث. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22240910)
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621483)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
小村 豪 (2026). تصميم أمن التحديث التلقائي - لماذا لا يكفي HTTPS. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621483 https://comcomponent.com/ar/blog/2026/04/09/000-comcomponent-autoupdate-security/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621483
- DOI (هذه النسخة)
- 10.5281/zenodo.22279942
المحتويات
- الخلاصة أولاً
- لماذا التحديث التلقائي منطقة خطرة
- الأنماط السيئة
- أفضل الممارسات
- الحد الأدنى من التكوين الآمن
- كيف نفكّر في مشاريع Windows
- الحد الأدنى من قائمة المراجعة
- خلاصة
- المراجع
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 27، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
1. الخلاصة أولاً
نرتّب أولاً مواضع الاستقرار في العمل.
- إن وافقت المتطلبات، ففضّل أولاً بنى التحديث القائمة مثل MSIX App Installer أو ClickOnce
- إن احتجت إلى updater خاص، فأول ما يُدخل ليس الواجهة بل التحقّق من التوقيع والتعافي عند الفشل
- تعامل مع معلومات التحديث مثل
latest.jsonبوصفها بيانات وصف موقّعة، لا ملف إعداد غير موقّع - TLS ضروري لكنه ليس شرطاً كافياً
- اجعل قرار التحديث ليس «لأن السيرفر يقول ذلك» بل «لأن العميل تحقّق وحكم بأنه صحيح»
- افصل مفاتيح التوقيع بين التطوير والإنتاج، واحمها بـ HSM أو خدمة توقيع
- عند فشل التحديث اجعل السلوك fail-closed لا fail-open
- updater بلا مواجهة للتراجع أأمن أن يُفترض أنه سيُعاد به إلى نسخة هشة
- إن كنت في مرحلة لا يمكن فيها بعد إدخال التحقّق من التوقيع، فتوزيع مثبت موقّع يدوياً أكثر أماناً من التحديث التلقائي
نثبّت المصطلحات هنا أولاً. ما يلي يستخدم بهذه المعاني.
| المصطلح | المعنى |
|---|---|
| fail-closed | تصميم يميل عند الشذوذ إلى «جانب التوقّف». إن فشل التحقّق من التوقيع لا يتقدّم التحديث |
| fail-open | تصميم يميل عند الشذوذ إلى «جانب التقدّم». يظهر تحذيراً فقط ويواصل التحديث |
| staging | أسلوب ينشر النسخة الجديدة أولاً في موضع منفصل، ثم يبدّل بعد اكتمال التحقّق |
| kill switch | آلية توقف التحديث الجاري فوراً بعملية من جانب السيرفر |
| trust anchor | نقطة الانطلاق التي يثق بها العميل من الأصل. مفتاح عام جذري، أو سلسلة شهادات مثبّتة |
باختصار، جوهر التحديث التلقائي ليس «كيف تنزّل»، بل «بمن تثق، وأين تتحقّق، وكيف تعود إن انكسر».
2. لماذا التحديث التلقائي منطقة خطرة
الميزات العادية تبقى مغلقة داخل التطبيق. في المقابل، يملك الـ updater الأمور الثلاثة التالية دفعة واحدة.
- يذهب لجلب ملفات من الخارج
- يثق بهذه الملفات
- يستبدل المنفّذات الموجودة
أي أن مسار تنفيذ شيفرة اعتباطية مدمج أصلاً داخل المنتج من البداية.
من الالتباسات الشائعة هنا قول «HTTPS، إذن آمن». بالتأكيد TLS ضروري. لكن ما يحميه أساساً هو مسار الاتصال وصحّة الجهة المتصل بها. أما أن يُخترق سيرفر التحديث ذاته، أو يُوضع منتج خاطئ على CDN رسمي، أو يُستبدل manifest غير موقّع، فهذه أمور لا يكفي TLS وحده فيها.
في الواقع، حتى التهديدات التي رتّبها TUF (The Update Framework. مواصفة تعرّف نموذج الثقة لتحديث البرمجيات) تتضمن لأنظمة التحديث ما يلي.
- إدخال برمجيات غير مشروعة اعتباطية
- تراجع (rollback) يعيد إلى نسخة قديمة هشة
- تجميد (freeze) يحجب النسخة الجديدة
- mix-and-match يخلط بيانات وصف ومنتجات غير متسقة فيما بينها
أي أن التحديث التلقائي ليس «نقل ملفات» بل «توزيع ثقة». وحين تُصمَّم هذه النقطة، يبدأ التحديث التلقائي بالعمل بأمان.
2.1 جدول مقابلة التهديد والمواجهة
التهديد والمواجهة موزّعان على فصول مختلفة في هذا المقال. نضع علاقة المقابلة أولاً في ورقة واحدة. هيكل التصميم يكفي بهذا.
| التهديد | ماذا يحدث | هل يكفي TLS لمنعه | المواجهة الرئيسة | الفصل التفصيلي |
|---|---|---|---|---|
| توزيع منتج غير مشروع | يُدخل ملف أعدّه المهاجم بوصفه تحديثاً رسمياً | لا يُمنع (عاجز أمام اختراق الأصل وسوء التوزيع) | بيانات وصف موقّعة، وتحقّق العميل من hash / توقيع المنتج | 4.2 / 4.3 / 4.4 |
| rollback | التوقيع صحيح، لكن يُعاد إلى نسخة قديمة ذات ثغرة معروفة | لا يُمنع (التوقيع وTLS كلاهما صحيح) | الاحتفاظ بأعلى رقم إصدار معروف ورفض ما هو أقدم | 4.8 |
| freeze | النسخة الجديدة صدرت، لكن بيانات وصف قديمة تُعاد باستمرار فلا يتم التحديث | لا يُمنع | جعل البيانات تحمل expires_at ورفض ما طال عليه الأمد |
4.3 / 4.8 |
| mix-and-match | يُمرَّر تركيب غير متسق من بيانات وصف ومنتج | لا يُمنع | تثبيت hash / size / الإصدار للمنتج المستهدف داخل الـ manifest | 4.3 / 4.8 |
| اختراق مفتاح التوقيع | يُوزَّع تحديث غير مشروع بتوقيع رسمي | لا يُمنع | فصل مفاتيح التطوير / الإنتاج، وHSM أو خدمة توقيع، ومسار موافقات وسجل تدقيق، وفصل مفتاح الجذر عن مفتاح البيانات | 4.5 |
| فشل التحديث أو انقطاعه | يسقط في منتصف الاستبدال فيتعذّر إقلاع التطبيق | خارج النطاق | staging + تفعيل ذري + تراجع | 4.6 |
| الالتفاف على التحقّق | يبقى منفذ مثل skipVerify في الإنتاج |
خارج النطاق | تثبيت fail-closed في المواصفات، وعدم حمل علم التفاف | 3.7 / 4.6 |
| تعذّر الإيقاف عند الحادثة | تواصل النسخة المشكلة التوزيع | خارج النطاق | blocklist، وminimum allowed version، وkill switch | 4.8 / 7 |
عمود «هل يكفي TLS لمنعه» كله «لا يُمنع» أو «خارج النطاق»، وهذا مقصد المقال. يحمي TLS المسار، أما حماية مضمون ما يُوزَّع وصلاحية التوزيع فآلية أخرى.
3. الأنماط السيئة
نلخّص أولاً الأشكال الخطرة التي تُرى كثيراً في العمل.
| النمط السيئ | ما الخطر فيه | الحد الأدنى من الإصلاح |
|---|---|---|
جلب version.json عبر HTTPS وتشغيل zip / exe من الـ URL مباشرة |
ضعيف أمام اختراق الأصل، واستبدال الإعدادات، وسوء التوزيع | حوّله إلى تحقّق العميل من بيانات وصف موقّعة ومن المنتج |
| توقيع الثنائي فقط، وترك الـ manifest بلا توقيع | يمكن العبث بـ URL والإصدار والقناة وعلامة التحديث الإلزامي | اجعله manifest موقّعاً يتضمن الإصدار / hash / size / القناة / الأجل |
| وضع مفاتيح التوقيع في ملفات على جهاز التطوير أو CI | عند الاختراق يمكن توزيع برمجيات خبيثة بتوقيع رسمي | HSM / خدمة توقيع + مسار موافقات + سجل تدقيق |
| «تجاهل خطأ التحقّق وتابع» عند فشل التحديث | يفتح أضعف مسار حين تقع الحادثة فعلاً | اجعله fail-closed |
| تحديث بالكتابة فوق دون الإبقاء على النسخة القديمة | انقطاع كهرباء، أو نقص قرص، أو فشل في المنتصف يجعل الإقلاع مستحيلاً | staging + تفعيل ذري + تراجع |
| السماح بنسخ قديمة بمجرد مقارنة الإصدارات | يمرّ التراجع إلى نسخة هشة | رقم إصدار متزايد رتيباً وحفظ أعلى نسخة معروفة |
| تشغيل الـ updater كاملاً بصلاحيات مدير | يتّسع نطاق الضرر عند الاختراق | اجعل التنزيل والتحقّق بصلاحيات منخفضة، وافصل الاستبدال فقط في helper بأقل صلاحيات |
| البدء بالتحديث التفاضلي | تعقيد التنفيذ يزيد فجوات التحقّق | ابدأ أولاً بتحديث حزمة كاملة |
فيما يلي نظرة بشيء من التفصيل.
3.1 التوقّف عند «HTTPS، إذن لا بأس»
هذا هو الأكثر شيوعاً.
- قراءة
latest.jsonعند الإقلاع - استخراج
downloadUrl - تنزيل zip / exe
- فك الضغط والاستبدال
- الإنهاء
من حيث الشكل يبدو معقولاً، لكن جذر الثقة يميل أكثر من اللازم نحو رد السيرفر. إذا اختُرق سيرفر التحديث أو إعدادات التوزيع، أمكن توزيع تحديث غير مشروع فوق HTTPS صحيح.
TLS ضروري. لكن TLS وحده لا يُكمل تصميم الـ updater.
3.2 التوقيع موجود، لكن العميل لا يتحقّق
حتى لو وقّعت الملفات عند الإصدار، إن لم ينظر إليها العميل فلا فائدة من التوقيع.
الشكل الشائع:
- التوقيع موجود في CI
- لكن الـ updater يفحص hash فقط
- وذلك الـ hash ذاته يأتي من manifest غير موقّع
في هذه الحالة، حين يُستبدل الـ manifest يُستبدل الـ hash معه. «أنا أفحص الـ hash، إذن آمن» لا يصح إلا إذا حُمي مصدر الـ hash نفسه أيضاً.
3.3 الـ manifest غير موقّع
ما ينبغي حمايته فعلاً في أنظمة التحديث ليس الملف التنفيذي وحده. المعلومات التالية على الأقل خطر العبث بها.
- الإصدار / معرّف الإصدار
- URL واسم الملف المراد تنزيله
- hash / size
- القناة (stable / beta وغيرها)
- هل هو تحديث إلزامي
- نظام التشغيل / المعمارية المنطبقان
- أجل صلاحية بيانات الوصف
- أدنى إصدار مطلوب للـ updater
أي أن الحس المناسب هو: ضع كل المعلومات المستخدمة في قرار التحديث ضمن بيانات وصف موقّعة.
3.4 إدارة مفاتيح التوقيع غير مرتّبة
أمان ميزة التحديث يعود بنسبة كبيرة إلى أمان إدارة المفاتيح.
إن كانت مفاتيح توقيع الإنتاج موضوعة على النحو التالي، فالخطر كبير.
- متروكة في مخزن الشهادات على جهاز التطوير
- مرفوعة كسر في CI على هيئة
.pfx - يجري توزيع المفتاح الخاص نفسه محلياً على عدة أشخاص
- توقيع التطوير وتوقيع الإنتاج في سلسلة الثقة نفسها
في هذه الحالة، حتى لو كان الـ updater صحيحاً، لا يمكن إيقاف «تحديث غير مشروع موقّع رسمياً».
3.5 تحديث بالكتابة فوق دون الإبقاء على النسخة القديمة
في التحديث، تصميم حالة الفشل أهم من تصميم حالة النجاح.
- انقطاع التنزيل في المنتصف
- فشل فك الضغط
- انقطاع التيار في منتصف الاستبدال
- إقلاع النسخة الجديدة لكن الترحيل الأولي يسقط
في هذه اللحظة، إن كانت النسخة القديمة قد حُذفت، يصعب التعافي. في العمل، «التطبيق توقّف عن العمل عند العميل» مشكلة أكبر من «فشل التحديث».
3.6 عدم التفكير في التراجع
حتى النسخ الرسمية الموقّعة قد تناسب المهاجم إن كانت قديمة وهشة.
مثلاً:
- في الإصدار 1.8 ثغرة معروفة
- العميل وصل إلى 2.3
- يعيد المهاجم توزيع 1.8
إن مرّ هذا، فالتوقيع نفسه صحيح لكنه خطر.
لا يكفي «هل هذا موقّع»، بل يجب رؤية «هل يجوز تثبيت هذه النسخة الآن».
3.7 fail-open
هذا أسوأ ما يجب فعله في الإنتاج.
- عند فشل التحقّق من التوقيع، إظهار تحذير فقط ومتابعة العمل
- وجود علم مخفي يسمح بتجاهل خطأ انتهاء صلاحية الشهادة
- بقاء
skipVerify=trueللتصحيح في الإنتاج أيضاً
في أوقات الأعطال أو الهجمات، تصبح هذه المنافذ هي المسار الرئيس.
4. أفضل الممارسات
4.1 ابدأ بالاعتماد على بنى التحديث القائمة
من الأكثر أماناً أن تشكّ أولاً في حقيقة الحاجة إلى updater خاص.
على Windows، طالما وافقت المتطلبات، يسهل تفضيل التالي.
- MSIX + App Installer
- ClickOnce
- Store / MDM / بنى التوزيع الداخلية
- MSI + إدارة توزيع من جانب المؤسسة
السبب بسيط: يمكن إزاحة جزء من مسؤولية التحديث ذاتها إلى المنصة. بالتأكيد تقل الحرية، لكن توافق واجهة التحديث وmanifest التوزيع وتوقيع الحزمة والتشغيل يصبح أيسر.
تنشأ الحاجة إلى updater خاص مثلاً في الحالات التالية.
- الرغبة في التحكّم الصارم بقنوات متعددة stable / beta / preview
- الرغبة في توزيع تدريجي ومعدل rollout
- الرغبة في التحكّم الدقيق بتوقيت التحديث لأسباب عمل خاصة
- وجود تكوين لا يتلاءم مع MSIX / ClickOnce
حتى في هذه الحالة، الفهم الأكثر استقراراً ليس «نريد حرية» بل «نتحمل مسؤولية التحديث بأنفسنا».
4.2 ضع نقطة انطلاق الثقة في جهة العميل
الـ updater الآمن لا يصدّق رد السيرفر كما هو. يحتاج جانب العميل على الأقل إلى الأمرين التاليين.
- مفتاح عام موثوق أو سلسلة شهادات
- آلية للتحقّق من بيانات وصف موقّعة بذلك المفتاح
بعبارة أخرى، يجب إيجاد حالة لا يكون فيها الحكم «السيرفر يقول إن هذه أحدث نسخة»، بل «يستطيع العميل تأكيد أن بيانات الوصف هذه صادرة عن موقّع موثوق ويعدّها أحدث نسخة».
نرسم كيف تتصل الثقة من الجذر حتى الملف.
flowchart TD
ROOT["مفتاح root (trust anchor يُضمَّن في العميل)"] --> SIGNKEY["مفتاح توقيع البيانات (مفوَّض من root ويُحدَّث كثيراً)"]
SIGNKEY --> META["بيانات وصف التحديث الموقّعة (version / url / hash / size / expiry)"]
META --> CHECK1{"التحقّق من التوقيع والأجل والإصدار"}
CHECK1 -- "NG" --> STOP["إيقاف التحديث (fail-closed)"]
CHECK1 -- "OK" --> DL["تنزيل المنتج إلى منطقة staging"]
DL --> CHECK2{"التحقّق من size / hash / توقيع الحزمة"}
CHECK2 -- "NG" --> STOP
CHECK2 -- "OK" --> ACT["التفعيل مع الإبقاء على النسخة القديمة"]
ACT --> HEALTH{"التحقّق من سلامة الإقلاع الأول"}
HEALTH -- "NG" --> RB["التراجع إلى النسخة القديمة"]
HEALTH -- "OK" --> DONE["اكتمال التحديث"]
ما تريد الإمساك به في الرسم نقطتان. من الأعلى إلى الأسفل، الثقة سلسلة واحدة. وحيثما انقطعت السلسلة، الوجهة هي إيقاف التحديث أو التراجع، لا التقدّم مؤقتاً.
4.3 صمّم حول بيانات الوصف الموقّعة بوصفها المحور
كحد أدنى، ضع البنود التالية ضمن بيانات وصف التحديث واجعلها هدف التوقيع.
| البند | سبب إدراجه |
|---|---|
| رقم الإصدار / معرّف الإصدار | منع التراجع، التدقيق |
| اسم المنتج، URL، نوع الحزمة | تثبيت الملف المستهدف |
| hash، size | كشف العبث، كشف التوزيع التالف |
| القناة | عدم خلط beta داخل stable |
| نظام التشغيل / المعمارية المستهدف | منع التوزيع الخاطئ |
| أدنى إصدار مطلوب للـ updater | إيقاف الـ updater القديم عند تغيير البروتوكول |
| expires_at | مواجهة التجميد |
| published_at | التدقيق، التشخيص |
| إلزامي / اختياري | منع العبث بفروع تجربة التحديث |
المهم هنا تجميع مواد قرار التحديث كلها في بيانات وصف موقّعة. الميل إلى وضع المنطق على العميل وحماية صحّة المعلومات بالتوقيع يقلّل الحوادث.
حتى لا يبقى الشكل غامضاً فلا يسقط في التنفيذ، نكتب مثالاً بالحد الأدنى. أولاً، المضمون الذي يكون هدف التوقيع.
{
"schema_version": 1,
"channel": "stable",
"release_version": "2.4.1",
"published_at": "2026-04-09T01:00:00Z",
"expires_at": "2026-04-16T01:00:00Z",
"minimum_updater_version": "2.0.0",
"minimum_allowed_version": "2.2.0",
"mandatory": false,
"artifacts": [
{
"os": "windows",
"arch": "x64",
"package_type": "msi",
"file_name": "MyApp-2.4.1-x64.msi",
"url": "https://updates.example.com/stable/MyApp-2.4.1-x64.msi",
"size": 48234496,
"sha256": "5f2c...64桁の16進..."
}
]
}
نلفّ هذا مع التوقيع.
{
"signed": {
"schema_version": 1,
"channel": "stable",
"release_version": "2.4.1",
"_comment": "上のオブジェクトをそのまま入れる"
},
"signatures": [
{
"keyid": "3f9a...",
"sig": "MEUCIQ..."
}
]
}
سبب هذا الشكل أن كل القيم المستخدمة في قرار التحديث داخل signed. URL والإصدار والقناة وعلامة الإلزام كلها في الداخل، لذا حتى لو استُبدل الخارج وحده سقط التحقّق. ثبّت ترتيب المعالجة في العميل على «التحقّق من signed → إن مرّ، استخدم القيم التي في داخله فقط». التنفيذ الذي يقرأ url ويبدأ التنزيل قبل التحقّق يفرّغ هذا الشكل من معناه.
في التنفيذ تنبيه واحد. JSON تتغيّر سلسلة بايتاته بترتيب المفاتيح أو طريقة وضع البياض. التحقّق من التوقيع يجري على سلسلة البايتات، لذا قرّر أولاً هل هدف التوقيع «تعبير مُطبَّع» أم «سلسلة البايتات المستلمة كما هي». إن لم تقرّر ذلك، وجعل جانب السيرفر يوقّع الكائن المولَّد وجانب العميل يوقّع نتيجة إعادة التسلسل، فشل التحقّق رغم أن التحديث صحيح. وبالمقابل، إن بدأت تتجاوز هذا الاختلاف بـ «لنمرّره»، فتلك أول خطوة نحو fail-open.
4.4 تحقّق من المنتج ذاته أيضاً
بعد التحقّق من بيانات الوصف، تحقّق أيضاً مما يلي في المنتج المنزَّل.
- size
- hash
- توقيع الحزمة / توقيع الشيفرة
- جهة الإصدار والمعرّف المتوقع
عند التعامل مع PE / MSI / MSIX على Windows، الأكثر أماناً افتراض إجراء التحقّق من Authenticode أو توقيع الحزمة في جانب العميل. أما على macOS، فالتمسّك بمتطلب Developer ID وnotarization حتى في مسار التحديث يجعل الأمر أكثر استقراراً.
4.5 احمِ المفاتيح بالتشغيل لا بالميزة
تظهر الفروق في إدارة المفاتيح في التشغيل أكثر منها في التنفيذ.
كحد أدنى، يفضّل الفصل التالي.
- مفتاح توقيع التطوير
- مفتاح توقيع الـ staging
- مفتاح توقيع الإنتاج
ثم، بالنسبة لمفاتيح الإنتاج، يستحسن تصميم ما يشمل:
- HSM
- خدمة توقيع سحابية
- نظام توقيع مع مسار موافقات
- سجل تدقيق
- إجراء تدوير المفاتيح
- توقيع مع ختم زمني
«إذا نجحت بناءات الإنتاج، يوقّع CI تلقائياً» مريح، لكن نصف قطر الضرر عند الاختراق يكبر أيضاً. ينبغي على الأقل أن يكون بالإمكان تتبع من وقّع ماذا ومتى.
عند نضوج التشغيل، فصل ثقة الجذر التي نادراً ما تتغيّر عن مفتاح بيانات وصف التحديث الذي يُعاد توقيعه باستمرار يزيد الأمان أكثر. تصميم يبقي الجذر أقرب إلى وضع دون اتصال ويستخدم مفتاحاً منفصلاً لبيانات وصف التحديث يجعل خفض نصف قطر الضرر عند تسرّب المفتاح أيسر.
4.6 fail-closed وتحديث مرحلي
تدفّق التحديث الأساسي بالترتيب التالي.
- جلب بيانات الوصف
- التحقّق من التوقيع والأجل والإصدار
- تنزيل المنتج إلى منطقة staging
- التحقّق من hash / size / التوقيع
- تجهيز التفعيل مع الإبقاء على النسخة القديمة
- التبديل عند إعادة التشغيل أو عبر helper مخصّص
- التحقّق من سلامة الإقلاع الأول
- التراجع عند وجود مشكلة
المهم هنا أمران: لا تستبدل قبل انتهاء التحقّق لا تتقدّم إن فشلت.
4.7 قلّص صلاحيات الـ updater
من المرغوب تجنّب تشغيل الـ updater كاملاً بصلاحيات مدير.
الفصل المثالي:
- التنزيل والتحقّق: صلاحيات منخفضة
- استبدال الملفات الفعلي فقط: helper بأقل صلاحيات
- لا يقوم الـ helper بأكثر من «وضع حزمة متحقَّق منها في المكان المحدد»
كلّما احتاج التصميم إلى رفع الصلاحيات، يصبح الخطر أكبر إذا لم يُفصل بوضوح ما الذي تحقّقت منه قبل الرفع.
4.8 سدّ التراجع / التجميد / mix-and-match من البداية
إضافة هذه الأمور لاحقاً مؤلمة، فمن الأفضل إدخالها أولاً.
-
مواجهة التراجع يحتفظ العميل بـ «أعلى إصدار لبيانات الوصف / رقم إصدار رآه حتى الآن»، ويرفض ما هو أقدم منها
-
مواجهة التجميد جعل بيانات الوصف تحمل أجلاً ورفض بيانات طال عليها الأمد
-
مواجهة mix-and-match ضمان الاتساق بين بيانات الوصف. على الأقل، ثبّت داخل الـ manifest نفسه hash / size / الإصدار للمنتج المستهدف
إضافة إلى ذلك، إمكان توزيع blocklist يرفض بناء معيناً أو minimum allowed version عبر بيانات وصف موقّعة يسرّع احتواء الحوادث.
حتى من دون اعتماد TUF كما هو، هذه الخصائص الثلاث مهمة جداً.
4.9 ابدأ من التحديث الكامل أولاً
التحديث التفاضلي مفيد لعرض الحزمة، لكنه معقّد كتنفيذ أولي.
- من أي نسخة قديمة إلى أي نسخة جديدة يطبَّق التفاضل
- hash المسبق قبل تطبيق التفاضل
- hash النهائي بعد تطبيق التفاضل
- التعافي عند الفشل في المنتصف
- التطبيق الجزئي وتنظيف التفاضلات القديمة
هذه الأمور تتزايد دفعة واحدة. في الإصدار الأولي، يكفي استبدال حزمة كاملة موقّعة بأمان.
5. الحد الأدنى من التكوين الآمن
حتى وإن لم نذهب إلى TUF كامل، فإن الحد الأدنى من التكوين الآمن لـ updater خاص يكون عادة كالتالي.
5.1 ما يحمله العميل
ما يحمله العميل هو مادة للشك في رد السيرفر. إن خلا هذا، تقرّر جواز التحديث بقول السيرفر وحده.
- المفتاح العام الجذري الموثوق، أو سلسلة شهادات مثبّتة
- الإصدار الجاري تشغيله حالياً
- أعلى إصدار لبيانات الوصف / رقم إصدار شوهِد سابقاً
- القناة المسموح بها
- النسخة السابقة المخصّصة للتراجع
5.2 ما يردّه السيرفر
ما يردّه السيرفر هو مادة الحكم لا الحكم نفسه. لا يُوثق بأي منها منفردة، ولا يصير لها معنى إلا بعد عبور التحقّق بمثبت الثقة في 5.1.
- بيانات وصف تحديث موقّعة
- منتج موقّع، أو موقّع من المنصة
- عند الحاجة، معلومات blocklist / minimum allowed version
5.3 التدفّق النموذجي
جلب بيانات الوصف
↓
التحقّق من التوقيع والأجل والإصدار والقناة
↓
تنزيل المنتج إلى staging
↓
التحقّق من size / hash / توقيع الحزمة
↓
التفعيل مع الإبقاء على النسخة القديمة
↓
التراجع إن فشل الإقلاع الأول
المهم هنا أن لا شيء يكتمل بمجرد رد سيرفر التحديث. ما يجعله يكتمل هو مثبت الثقة الذي يحمله العميل ومنطق التحقّق.
6. كيف نفكّر في مشاريع Windows
في تطبيقات Windows، الترتيب الأسهل هو الانطلاق عكسياً من طريقة التوزيع.
- إن وافقت المتطلبات فاستخدم MSIX App Installer
- إن كان تطبيق .NET داخلياً بنطاق per-user مناسباً فـ ClickOnce
- إن احتجت إلى خدمة أو برنامج تشغيل أو امتداد صدفة أو تحكّم بقناة خاصة، فقارن أيضاً MSI + updater خاص
غير أن اختيار updater خاص لا يقلّل ما ينبغي فعله. بل يزيده.
- التحقّق من Authenticode / توقيع الحزمة
- manifest موقّع
- مواجهة التراجع
- فصل صلاحيات helper التحديث
- استراتيجية تحديث الـ updater ذاته
6.1 كيف يتحقّق العميل من توقيع Authenticode
كتابة «نتحقّق من Authenticode» وحدها لا تسقط في التنفيذ، لذا نذكر مداخل Windows.
| المراد | الوسيلة |
|---|---|
| التحقّق من داخل شيفرة الـ updater | واجهة WinVerifyTrust (wintrust.dll). تعيين WINTRUST_ACTION_GENERIC_VERIFY_V2 يطبّق سياسة تحقّق Authenticode |
| التأكيد من إجراء تشغيل أو من CI | Get-AuthenticodeSignature في PowerShell |
| التوقيع والتأكيد في عمل الإصدار | signtool sign / signtool verify في Windows SDK |
في PowerShell، الحد الأدنى من التأكيد هو هذا فقط.
$path = ".\MyApp-2.4.1-x64.msi"
$sig = Get-AuthenticodeSignature -FilePath $path
if ($sig.Status -ne 'Valid') {
throw "signature check failed: $($sig.Status) / $($sig.StatusMessage)"
}
# «التوقيع صالح» لا يكفي. ثبِّت أيضاً توقيع من.
# غير أن Subject (الاسم المميّز) ليس فريداً. شهادة بنفس CN/O/C
# يمكن إصدارها من أي CA آخر، وإن وثقت هذه المحطة بذلك CA
# يصير Status هو Valid وتمر مقارنة Subject أيضاً.
# ما ينبغي تثبيته هو «سلسلة جهة الإصدار» أو «المفتاح العام»، وSubject معاون.
$expectedIssuers = @( # بصمات إبهام جهة الإصدار (CA وسيط/جذر)
'9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B' # ← استبدل بالقيمة الفعلية
)
$expectedSubject = 'CN=Example Software Inc., O=Example Software Inc., C=JP'
# السلسلة التي تُعاد بناؤها هنا لـ «تتبّع جهة الإصدار» فقط،
# وحكم الثقة اكتمل أعلاه بـ Status = Valid.
# VerificationTime في X509ChainPolicy افتراضه لحظة استدعاء المنشئ
# (= الحاضر)، فإن نفّذت Build كما هو يفشل بـ NotTimeValid
# لحظة انتهاء أجل شهادة التوقيع. التوقيع المختوم زمنياً يبقى Valid
# بعد الانتهاء، فإن أبقيت الافتراضي صار الـ updater «يرفض كل الإصدارات
# السابقة التي تحقّقت صحيحاً، لحظة عبور تجديد الشهادة».
# إن عرفت وقت التوقيع فأدخله في VerificationTime. وإن لم تعرف،
# فحكم الأجل اكتمل أعلاه فلا تنظر إليه في هذا الـ Build
$chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new()
$chain.ChainPolicy.VerificationFlags = 'IgnoreNotTimeValid'
$chain.ChainPolicy.RevocationMode = 'NoCheck'
try {
$built = $chain.Build($sig.SignerCertificate)
# أن توجد جهة الإصدار المتوقَّعة في موضع ما من الموقِّع إلى الجذر.
# عند فشل Build لا تُملأ السلسلة إلا جزئياً،
# فيسقط هذا الحكم أيضاً في تلك الحالة (fail-closed)
$chainThumbprints = @($chain.ChainElements | ForEach-Object { $_.Certificate.Thumbprint })
if (-not ($expectedIssuers | Where-Object { $chainThumbprints -contains $_ })) {
throw "unexpected issuing chain (built=$built): $($chainThumbprints -join ' / ')"
}
}
finally {
$chain.Dispose()
}
if ($sig.SignerCertificate.Subject -ne $expectedSubject) {
throw "unexpected signer: $($sig.SignerCertificate.Subject)"
}
عندئذٍ، قيود تريد الإمساك بها خمسة.
- حتى إن كان
StatusهوValid، فالمعنى فقط «صحيح بوصفه توقيعاً». من وقّع أمر يُؤكَّد على حدة - تطابق
Subjectلا يثبت «أنه الشخص المعني». الاسم المميّز ليس معرّفاً فريداً. سواء من مرجع شهادات داخلي أو عام، يمكن إصدار شهادة توقيع شيفرة بالاسم المميّز نفسهCN=Example Software Inc., O=Example Software Inc., C=JP. إن وثق العميل بذلك المرجع، مرّت نسخة بديلة موقّعة بمفتاح آخر ومن جهة إصدار أخرى، بـStatus = ValidوبتطابقSubject. ما تثبّته هو سلسلة جهة الإصدار (أن بصمة المرجع الوسيط / الجذر المتوقع واردة في المسار من الموقّع إلى الجذر) أو المفتاح العام، وتستخدمSubjectتضييقاً فوق ذلك - بعد التثبيت، قرّر إجراء التبديل مسبقاً. وإلا توقّف تحديث كل الأجهزة في يوم تبديل الشهادة أو المرجع. احمل القيم المتوقعة حتماً كمصفوفة، واصنع حالة تقبل القديم والجديد جنباً إلى جنب قبل التبديل (ثلاث مراحل: وزّع أولاً updater يضيف جهة الإصدار الجديدة إلى قائمة السماح → بدّل التوقيع → أسقط القيمة القديمة). «التوزيع أولاً» ممكن فقط حين يمكن تحديث الـ updater ذاته، لذا صمّمه وحدة مع «استراتيجية تحديث الـ updater ذاته» المذكورة في صدر الفصل 6
- التوقيع بلا ختم زمني يتوقف عن المرور في التحقّق لحظة انتهاء أجل الشهادة. أرفق حتماً ختماً زمنياً عند الإصدار. سبب ذكر التوقيع المختوم زمنياً في 4.5 هو هذا
- لا تجعل
X509Chainالمستخدم لتتبع جهة الإصدار يعيد الحكم على مدة الصلاحية بالوقت الحالي. القيمة الافتراضية لـX509ChainPolicy.VerificationTimeهي لحظة استدعاء المنشئ، أي الحاضر. تنص الوثائق أيضاً على أن «عند التحقّق من رسالة موقّعة، يجب أن يكون التوقيع صالحاً وقت التوقيع لا وقت التحقّق، لذا تصير هذه الخاصية مهمة». إن أبقيتها على الافتراضي، رفضت الحزمة التي صارStatus = Validفيها بفضل الختم الزمني، مباشرة بعدها، بوصف «الشهادة منتهية». يضيع معنى الختم الزمني، وتسقط كل الإصدارات السابقة في لحظة تحديث الشهادة. إن عرفت وقت التوقيع فأدخله فيVerificationTime، وإلا فأرفقIgnoreNotTimeValid. هذا ليس تخفيفاً. حكم المدة والثقة اكتمل فيStatus = Validأعلاه، وهذاBuildلمعرفة «من أصدر» فقط. للسبب نفسه لا نجري هنا تأكيد الإلغاء (قائمة الإلغاء لشهادة منتهية لا يُضمن استمرار إصدارها أصلاً). وسيلة الإيقاف عند تسرّب المفتاح ليست CRL بل blocklist وminimum allowed version (4.8)
واعلم أن نتيجة التحقّق تعتمد على مخزن الشهادات وإعدادات الثقة في ذلك الجهاز. في بيئة إعداد ثقة العميل فيها متراخٍ، يتراخى معنى Status = Valid أيضاً. إن حمل الـ updater نفسه جهة الإصدار المتوقعة كما أعلاه، لم يتأثر حتى لو اتسع إعداد الثقة على الجهاز.
الشكل الخطر الشائع على Windows هو خط مستقيم على هيئة DownloadFile -> unzip -> kill process -> overwrite -> restart.
قد يعمل، لكنه ضعيف من ناحيتي الأمان والقدرة على التعافي معاً.
التشغيل الذي يدفع المستخدم لتجاوز تحذيرات SmartScreen أو UAC عبر «معلومات إضافية → تشغيل» ليس تصميماً للتحديث، بل تطبيعاً للتحذيرات. لإنشاء مسار تحديث صحيح، لا تجعل المستخدم يعتاد التحذيرات، بل اقترب من تكوين توزيع وتحقّق لا يُنتج تحذيرات بسهولة.
نرتّب مقارنة طرق التوزيع نفسها أيضاً في المقال التالي. كيف تختار نموذج توزيع تطبيق Windows - MSI و MSIX و ClickOnce و xcopy والـ custom updaters
7. الحد الأدنى من قائمة المراجعة
قبل إخراج updater خاص، تحقّق على الأقل مما يلي.
- بيانات وصف التحديث موقّعة
- بيانات الوصف تحتوي على الإصدار / hash / size / القناة / الأجل
- جانب العميل يتحقّق من التوقيع والإصدار
- تثبيت الموقّع يتم بسلسلة جهة الإصدار أو المفتاح العام لا بالاسم المميّز (Subject)
- إجراء تبديل القيم المثبّتة (فترة قبول القديم والجديد جنباً إلى جنب) مقرّر
- يجري التحقّق من hash المنتج وتوقيع المنصة
- مفتاح توقيع الإنتاج مفصول عن بيئة التطوير
- يبقى سجل استخدام المفاتيح ووثائق الموافقة
- يجري استخدام توقيع مع ختم زمني
- في تحديث الـ staging، يجري التبديل مع الإبقاء على النسخة القديمة
- هناك شروط وإجراءات للتراجع
- عند فشل التحقّق، يتوقّف بـ fail-closed
- هناك سياسة لتحديث الـ updater ذاته
- يمكن توزيع blocklist / minimum allowed version
- هناك kill switch لإيقاف التوزيع التدريجي
- يمكن مراقبة معدل الفشل ومعدل التراجع وفشل التحقّق من التوقيع
إن كانت هذه القائمة تحوي فجوات كثيرة، فإن إحكام نموذج ثقة التوزيع أولاً أنفع من بناء واجهة الـ updater.
8. خلاصة
يمكن تكثيف أمن ميزة التحديث التلقائي في الجملة التالية.
صمّم لا «راحة التحديث»، بل بمن نثق، وكيف يتحقّق العميل من تلك الثقة.
ثم، بصياغة فضفاضة للحكم العملي:
- إن كفت البنى القائمة، فابدأ بالاعتماد عليها
- إن كنت ستبني updater خاصاً، فأدخل بيانات وصف موقّعة وإدارة المفاتيح قبل HTTPS
- updater لا يُصمَّم له تعافٍ من الفشل وتراجع يكون مؤلماً في الإنتاج
- الـ updater ليس ميزة توزيع، بل هو الحد الأمني للمنتج ذاته
إن كان تكوينك الحالي قريباً من latest.json + zip swap، فإن أول ما ينبغي إصلاحه ليس معالجة التنزيل بل طريقة وضع الثقة.
مجرد إصلاح هذه النقطة يغيّر مستوى الخطر تغييراً كبيراً.
9. المراجع
- CISA Secure by Design Pledge
- NIST: Security Considerations for Code Signing
- NIST Secure Software Development Framework (SSDF)
- The Update Framework Specification
- TUF: Roles and metadata
- TUF: Security
- Microsoft Learn: Authenticode Digital Signatures
- Microsoft Learn: WinVerifyTrust function
- Microsoft Learn: Get-AuthenticodeSignature
- Microsoft Learn: Auto-update and repair apps - MSIX
- Microsoft Learn: ClickOnce Deployment and Security
- Apple Developer: Developer ID
- CA/Browser Forum: Baseline Requirements for the Issuance and Management of Publicly-Trusted Code Signing Certificates
موضوعات ذات صلة
هذه صفحة موضوع قريبة من هذا الموضوع. انطلاقاً من المقال يمكنك الانتقال إلى الخدمات أو المقالات الأخرى ذات الصلة.
موضوعات تقنية عن Windows
نقطة الدخول التي تجمع الموضوعات التقنية عن تطوير Windows وتحقيق الأعطال والاستفادة من الأصول القائمة.
الخدمات التي يتصل بها هذا الموضوع
تطوير تطبيقات Windows
التحديث التلقائي ليس حديث واجهة فقط، بل تصميم يشمل طريقة التوزيع والصلاحيات والتعافي والتشغيل. يمكننا التعامل بدءاً من ترتيب طريقة التحديث في تطوير تطبيقات Windows الجديدة أو مراجعة البرمجيات القائمة.
الاستشارة التقنية ومراجعة التصميم
يمكن الاستشارة بدءاً من مرحلة الترتيب لأسئلة مثل «هل نحتاج إلى updater خاص؟» و«هل يكفي MSIX / ClickOnce؟» و«أين الخطر في تصميم التحديث الحالي؟».
ملف المؤلف
غو كومورا
ممثل شركة كومورا سوفت ذ.م.م.
يتمحور حول تطوير برمجيات Windows والاستشارة التقنية وتحقيق الأعطال، مع نقاط قوة في المشاريع التي تبقى فيها أصول قائمة وفي تحقيق أعطال يصعب رؤية أسبابها.
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
لماذا تظهر رسالة «قامت Windows بحماية جهاز الكمبيوتر الخاص بك»
نرتّب من منظور عمليّ أسباب ظهور تحذير SmartScreen عند توزيع تطبيقات Windows، بدءاً من توقيع الشيفرة وشهادات EV/OV و Azure Artifact Signin...
كيف تختار أسلوب توزيع تطبيق Windows - MSI وMSIX وClickOnce وxcopy والتحديث الخاص
أسلوب توزيع تطبيق Windows ليس تفضيل صيغة المثبِّت، بل اختيار درجة الارتباط بنظام التشغيل ومسؤولية التحديث. تنظّم المقالة MSI وMSIX وClick...
الأنابيب المسمّاة عمليّاً ── IPC القياسيّ في Windows من التصميم إلى الأمان
دليل عمليّ للأنابيب المسمّاة، وسيلة IPC القياسيّة في Windows. يغطّي وضع البايت مقابل وضع الرسالة، وخوادم متعدّدة العملاء، وأمان ACL وانتح...
CI/CD عمليّ لتطبيقات WinForms / WPF ── أتمتة البناء والتوقيع والتوزيع عبر GitHub Actions
دليل عمليّ لبناء CI/CD لتطبيقات WinForms / WPF عبر GitHub Actions. يشمل YAML الأدنى للبناء والاختبار على windows-latest، وترقيم الإصدار ا...
عندما يُعامَل تطبيق Windows الذي طوّرته شركتك بوصفه فيروساً ── التعامل مع الكشف الخاطئ في Microsoft Defender والتعايش مع أثره على الأداء
نرتّب الإجراء الرسميّ عند كشف Microsoft Defender تطبيق Windows الذي طوّرته شركتك خطأً. نشرح آليّة برامج مكافحة الفيروسات الحديثة، والإبلا...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
لأنّ توزيع تطبيقات Windows وتحديثها وتوقيعها وrollback واختيار MSIX / ClickOnce أمور يلزم التفكير فيها لا على مستوى التنفيذ وحده، بل بما يشمل أسلوب التوزيع وتصميم التشغيل.
الاستشارات التقنية ومراجعة التصميم
لأنّ تصميم حدود الثقة في التحديث التلقائيّ، وsigned metadata، وإدارة المفاتيح، وسلوك fail-closed، يحتاج إلى ترتيب البنية العامّة أكثر من حاجته إلى معالجة كلّ جزء من التنفيذ على حدة.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- أليس التحديث التلقائي آمناً ما دام الاتصال عبر HTTPS؟
- TLS ضروري لكنه ليس شرطاً كافياً. ما يحميه TLS أساساً هو مسار الاتصال وصحّة الجهة المتصل بها، ولا يكفي حين يُخترق سيرفر التحديث نفسه، أو يُوضع منتج خاطئ على CDN رسمي، أو يُستبدل manifest غير موقّع. قرار التحديث يجب ألا يكون «لأن السيرفر يقول ذلك»، بل «لأن العميل تحقّق من بيانات الوصف الموقّعة وحكم بأنها صحيحة».
- ماذا نضع في بيانات وصف التحديث ونوقّعه؟
- الأساس تجميع مواد قرار التحديث كلها في signed metadata. عملياً: رقم الإصدار، وURL المنتج واسم الملف، وhash وsize، والقناة (stable / beta وغيرها)، ونظام التشغيل والمعمارية المستهدفين، وأدنى إصدار مطلوب للـ updater، وأجل صلاحية البيانات (expires_at)، وعلامة التحديث الإلزامي. إن وقّعت الثنائي وحده وتركت الـ manifest بلا توقيع، بقي مجال للعبث بـ URL أو الإصدار أو علامة الإلزام.
- ما هجوم التراجع (rollback) وكيف يُمنع؟
- هو هجوم يعيد المهاجم فيه توزيع نسخة قديمة موقّعة رسمياً لكنها ذات ثغرة معروفة، ليعيد العميل إلى تلك النسخة الهشة. التوقيع نفسه صحيح، لذا لا يكفي التحقّق من التوقيع وحده. المواجهة أن يحتفظ العميل بأعلى رقم إصدار رآه حتى الآن، ويرفض ما هو أقدم. ومع ذلك اجعل لبيانات الوصف أجلاً حتى تمنع هجوم التجميد الذي يحجب النسخة الجديدة، وثبّت في الـ manifest قيم hash وsize والإصدار للمنتج حتى تسد هجوم mix-and-match.
- هل نبني updater خاصاً أم نستخدم آلية قائمة؟
- إن وافقت المتطلبات، فالأكثر أماناً تفضيل بنى التحديث القائمة مثل MSIX App Installer أو ClickOnce أولاً، لأن نطاق مسؤولية التحديث يُزاح إلى المنصة. الحاجة إلى updater خاص تنشأ حين توجد متطلبات لا تركب على البنى القائمة، مثل التحكّم الصارم بقنوات متعددة أو التوزيع التدريجي. حتى عندئذٍ، أول ما يُدخل ليس الواجهة بل التحقّق من التوقيع والتعافي عند الفشل، مع الالتزام بـ fail-closed أي عدم التقدّم إن فشل التحقّق، والإبقاء على النسخة القديمة حتى يمكن التراجع.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.