مدونة FireAI للأمان

بقلم FireAI Security & Research Team · نُشر في

تحقق من توقيع أي تطبيق Mac وتوثيقه من الطرفية

تحقق من توقيع أي تطبيق Mac وتوثيقه من الطرفية

يُجري Gatekeeper هذا الفحص تلقائيًا في المرة الأولى التي تفتح فيها تطبيقًا مُنزَّلًا، وفي معظم الأحيان لا ترى التفاصيل أبدًا — يظهر مربع حوار، تنقر عليه، وينتهي الأمر. هذا التمرين عن رؤية ما رآه Gatekeeper: أي مطوّر وقّع التطبيق، وهل توقيعه سليم، وهل تحققت منه خدمة التوثيق (notary) الخاصة بـ Apple، وما المسموح له بفعله بمجرد تشغيله. كل أمر هنا يأتي مع أدوات سطر أوامر Xcode (xcode-select --install إن لم تكن مثبَّتة بعد لديك)، وكل واحد منها للقراءة فقط — أنت تفحص التطبيق، لا تغيّره.

التوقيع نفسه: codesign -dvvv

codesign هي أداة Apple لإنشاء توقيعات الكود وفحصها. يعرض الخيار -d معلومات عن الكود الموقّع في مسار ما، ووفق دليل استخدامها، «مستويات أعلى من الإسهاب تنتج مخرجات أكثر» — لذا فإن -dvvv (عرض، بثلاثة مستويات من الإسهاب) يمنحك الصورة الكاملة بأمر واحد:

الطرفية — تفاصيل التوقيع الكاملة
codesign -dvvv /Applications/Example.app
# example output, trimmed to the fields that matter
Executable=/Applications/Example.app/Contents/MacOS/Example
Identifier=com.example.app
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=... flags=0x10000(runtime) hashes=...
Signature size=4741
Authority=Developer ID Application: Example Software LLC (ABCDE12345)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Team Identifier=ABCDE12345
Runtime Version=14.0.0

أربعة حقول ينبغي قراءتها في كل مرة. Authority هي سلسلة الشهادات: ينبغي أن ينتهي تطبيق خارجي عادي بـApple Root CA عبر Developer ID Certification Authority، مع تسمية المطوّر في السطر العلوي. Team Identifier هو معرّف Apple المكوّن من عشرة أحرف لحساب المطوّر — وهو القيمة التي تقارنها عبر التطبيقات التي تعتقد أنها من الشركة نفسها، لأنه لا يتغيّر بين إصداراتها. تعني flags=0x10000(runtime) أن البيئة المُحصَّنة (hardened runtime) مفعّلة، وهي مجموعة قيود إضافية (مثل مقاومة حقن الكود في العملية) تشترطها Apple للتوثيق (notarization). أما غياب أي سطر Authority تمامًا — فقط Signature=adhoc — فيعني أن التطبيق غير موقّع أو موقّع ذاتيًا دون أي سلسلة تصل إلى Apple إطلاقًا.

هل لا يزال التوقيع مطابقًا للملفات: --verify --deep --strict

التوقيع وعد بشأن مجموعة محددة من البايتات وقت التوقيع. يتحقق --verify مما إذا كان ذلك الوعد لا يزال قائمًا — فوفق دليل الاستخدام، يؤكد «أن الكود في تلك المسارات موقّع، وأن التوقيع صالح، وأن كل المكوّنات المختومة لم تُغيَّر». هناك خياران إضافيان مهمّان بالنسبة لحزمة تطبيق، وهي دليل مليء بموارد وأطر عمل وملفات تنفيذية مساعدة متداخلة، لا ملف واحد:

الطرفية — تحقق عميق وصارم
codesign --verify --deep --strict --verbose=2 /Applications/Example.app
/Applications/Example.app: valid on disk
/Applications/Example.app: satisfies its Designated Requirement

يهم الخيار --deep لأن التحقق من المحتوى المتداخل، وفق دليل الاستخدام، يقتصر افتراضيًا على «فحص سطحي قد لا يكشف تغييرات في الكود المتداخل» — بينما يتحقق الوضع العميق بشكل متكرر من كل إطار عمل وأداة مساعدة مضمَّنة، لا الحزمة الخارجية فقط. ويضيف --strict فحوصًا إضافية تعتبرها Apple مهمة بما يكفي لألا تكون مفعّلة افتراضيًا، من بينها أن أي رابط رمزي (symlink) داخل الحزمة «يشير إلى ملفات مختومة داخل حزمتها»، فيرفض أي رابط يشير إلى خارج التطبيق أو إلى شيء غير مختوم — وهي حيلة معروفة لتهريب حمولة غير موقّعة داخل حزمة موقّعة بشكل مشروع في ظاهرها. إن فشل أي من الفحصين، سترى code failed to satisfy specified code requirement أو ملاحظة تسمّي بالضبط أي عنصر متداخل لا يطابق ما خُتم أصلًا — اقرأ ذلك السطر، فهو يسمّي الملف.

قراءة الصلاحيات

الصلاحيات (entitlements) هي الأذونات المحددة التي يمنحها توقيع التطبيق له — الوصول إلى الكاميرا، والقدرة على الوصول إلى الشبكة خارج الصندوق المعزول (sandbox)، وتعطيل التحقق من المكتبات، وما إلى ذلك. يستخرجها codesign -d --entitlements -؛ ووفق دليل الاستخدام، «تُستخرج بيانات الصلاحيات المضمَّنة بالمثل وتُكتب إلى» المسار المُعطى، وتعني - المخرج القياسي:

الطرفية — الصلاحيات المُعلَنة لتطبيق
codesign -d --entitlements - /Applications/Example.app
# example output, trimmed
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>

معظم الصلاحيات عادية وتطابق ما يحتاجه التطبيق بوضوح — طلب تطبيق مكالمات فيديو الوصول إلى الكاميرا ليس اكتشافًا يستحق التوقف عنده. الصلاحية التي تستحق التوقف هي disable-library-validation: تعني أن التطبيق سيحمّل كودًا من خارج حزمته الموقَّعة نفسها، وهي حاجة عادية بالنسبة لبعض البرمجيات القائمة على الإضافات، وباب أوسع مما يحتاجه معظم التطبيقات. هذا تفصيل يستحق الملاحظة، لا علامة تحذيرية تلقائية، وهو بالضبط نوع التفاصيل التي لا يمكنك رؤيتها دون أن تسأل عنها.

هل تحققت خدمة التوثيق من Apple منه فعليًا: spctl وstapler

التوقيع الصالح يثبت فقط أن التطبيق لم يُغيَّر منذ أن وقّعه المطوّر — ولا يقول شيئًا عمّا إذا كانت Apple قد نظرت فيه. وهذا ما يضيفه التوثيق (notarization). يصف توثيق Apple نفسه خدمة التوثيق الآلية بأنها تفحص «برنامجك بحثًا عن مكوّنات ضارة، وتتحقق من مشكلات توقيع الكود، وتعيد إليك النتائج بسرعة». وحين ينجح الفحص، «تُنشئ خدمة التوثيق تذكرة لك لتُلصقها ببرنامجك؛ كما تنشر خدمة التوثيق تلك التذكرة عبر الإنترنت حيث يمكن لـ Gatekeeper إيجادها».

spctl --assess هي الطريقة العملية لطلب حكم محرك سياسات Gatekeeper نفسه بدلًا من استنتاجه بنفسك. ووفق دليل الاستخدام، يقوم --assess بـ«إجراء تقييم على الملفات المُعطاة»، ويُوصَف الخيار -v / --verbose، عند تكراره لمزيد من التفصيل، ببساطة بأنه يطلب «مخرجات أكثر إسهابًا»:

الطرفية — تقييم Gatekeeper نفسه
spctl --assess -vv /Applications/Example.app
/Applications/Example.app: accepted
source=Notarized Developer ID

source=Notarized Developer ID هي النتيجة التي تريد رؤيتها — تعني أن Gatekeeper وجد توقيع Developer ID صالحًا وتذكرة توثيق، سواء كانت تلك التذكرة ملصَقة بالتطبيق أو وُجدت عبر الإنترنت. أما نتيجة source=Unnotarized Developer ID فتعني أن التطبيق موقّع لكن Apple لم توثّقه (أو لم توثّقه بعد)، ونتيجة rejected مباشرة تعني أن Gatekeeper سيمنع تشغيله في إعداده الافتراضي.

للتحقق من التذكرة الملصَقة تحديدًا، يبحث xcrun stapler validate عن التذكرة المرفقة فعليًا بالتطبيق بدلًا من مطالبة Gatekeeper بالبحث عنها عبر الإنترنت — وهذا مفيد للتأكد من أن التطبيق سيُقيَّم بشكل صحيح حتى دون أي اتصال بالإنترنت إطلاقًا:

الطرفية — التحقق من التذكرة الملصَقة
xcrun stapler validate /Applications/Example.app
Processing: /Applications/Example.app
The validate action worked!

علامة الحجر الصحي: من أين يأتي فحص التشغيل الأول

يوضح دليل أمان منصة Apple أن «Gatekeeper يتتبع أيضًا مصدر الملفات المكتوبة بواسطة برامج مُنزَّلة» و«يطلب موافقة المستخدم قبل فتح برامج مُنزَّلة للمرة الأولى». والآلية وراء ذلك سمة موسَّعة، com.apple.quarantine، يرفقها Safari وMail وتطبيقات أخرى بأي شيء تحفظه من الشبكة. يسردها xattr -l، ووفق دليل الاستخدام، يتسبب هذا الخيار في «عرض كل من أسماء السمات وقيمها المقابلة»:

الطرفية — التحقق مما إذا كان ملف لا يزال مُعلَّمًا كمُنزَّل
xattr -l ~/Downloads/Example.dmg
com.apple.quarantine: 0081;65e1a2b3;Safari;

عدم وجود أي مخرجات إطلاقًا يعني أن الملف لا يحمل علامة حجر صحي — إما أنه أُنشئ محليًا، أو وصل عبر مسار لا يضبط تلك السمة (بعض أدوات الأرشفة ومديري الحزم تتجاوزها)، أو أُزيلت العلامة يدويًا بـxattr -d com.apple.quarantine. تستحق تلك الحالة الأخيرة المعرفة لسبب مختلف: فهي طريقة موثّقة يتجاوز بها الناس فحص التشغيل الأول من Gatekeeper كليًا، على ملفات يثقون بها أو يظنون أنهم يثقون بها، وتستحق أن تكون متعمَّدة لا نسخًا ولصقًا من مجموعة تعليمات طرفية غير مألوفة.

مقارنة عملية: تطبيقان يدّعيان المطوّر نفسه

طريقة ملموسة لاستخدام كل هذا معًا: لنفترض أن لديك نسختين من تطبيق تدّعيان كلتاهما أنهما من الشركة نفسها، واحدة من موقع المطوّر نفسه وأخرى من رابط أرسله لك أحدهم. شغّل codesign -dvvv على كلتيهما وقارن سطر Team Identifier — إنه سلسلة من عشرة أحرف مرتبطة بحساب مطوّر Apple محدد، وعلى عكس اسم عرض أو معرّف حزمة، ليس شيئًا يمكن لطرف آخر إعادة إنتاجه ببساطة دون الوصول إلى شهادة التوقيع الخاصة بذلك الحساب. إن أظهرت النسختان معرّفَي فريق مختلفين، فأنت لا تنظر إلى إصدارين من التطبيق نفسه؛ بل إلى موقّعين مختلفين، أحدهما ليس من يدّعيه التنزيل. أتبع ذلك بتشغيل spctl --assess -vv على النسخة المشبوهة — مصدر توثيق غير متطابق أو غائب على النسخة التي تدّعي أنها مطابقة لأصل موثَّق هو التأكيد، لا مجرد دليل.

قراءة الصورة الكاملة، والعلامات التحذيرية الفعلية

  • عدم وجود سلسلة Authority إطلاقًا، أو سلسلة لا تنتهي بـ Apple Root CA — لا توجد هوية مطوّر مسؤولة وراء التطبيق.
  • فشل codesign --verify، خصوصًا برسالة تسمّي ملفًا متداخلًا محددًا — شيء داخل الحزمة تغيّر بعد توقيعها.
  • يُعيد spctl --assess نتيجة rejected، أو لا يطابق معرّف الفريق في codesign -dvvv المطوّر الذي تتوقعه لذلك المنتج.
  • علامة حجر صحي أُزيلت بوضوح من ملف لم تُنزّله بنفسك، أو وصل عبر قناة غير معتادة (سكربت، مرفق بريد إلكتروني أُعيدت تسميته ليبدو شيئًا آخر).
  • صلاحيات مفتوحة على مصراعيها — وصول كامل إلى القرص، تعطيل التحقق من المكتبات، وصول شبكي غير مقيَّد — على تطبيق لا يحتاجها بوضوح لغرضه المُعلَن.

لا شيء من هذه الفحوص ينظر إلى ما يفعله التطبيق فعليًا بمجرد تشغيله واتصاله بالشبكة — فهذا سؤال من نوع مختلف، تجاب عنه بمراقبة حركته لا توقيعه. توقيع نظيف وتذكرة توثيق صالحة أرضية حقيقية وذات معنى: فهي تقول إن Apple رأت هذه المجموعة المحددة من البايتات ولم تجد مشكلات توقيع كود وقت الفحص. إنها بداية الثقة بتطبيق، لا نهايتها.

دور FireAI وHisnLabs

A clean signature and a stapled ticket say an app hasn’t been tampered with since Apple checked it — they say nothing about what it connects to afterward, which is the question FireAI’s per-app rules and on-device review are built to keep answering, signature by signature, connection by connection.

FireAI هو منتج HisnLabs نفسها: جدار حماية بذكاء اصطناعي يعمل على جهاز Mac مباشرة. يعرض بلغة واضحة كل اتصال تجريه تطبيقاتك، ويترك لك القرار فيما يخرج من جهازك — ذكاؤه الاصطناعي يعمل محليًا، فلا يُرسَل ترافيكك إلينا ولا إلى أي جهة أخرى أبدًا. فريق أبحاث الأمن في HisnLabs هو من يحافظ على دقة هذه القرارات: يصنّف النطاقات بين قياس عن بُعد عادي وخدمة حقيقية، ويتتبّع بلد وشبكة كل اتصال، ويدرّب النموذج المحلي (ميزة Autopilot) على حركة اتصال حقيقية، دون أن يغادر أي شيء جهاز Mac.

يمكنك الاطلاع على القرارات التقنية وراء ذلك، أو تجربة FireAI لمدة 17 يومًا، على FireAI من HisnLabs.

المصادر