مفتاح Bytecode Array Encoding
زوِّد مفتاح تشفير بايت كود الآلة الافتراضية الخاص بك عبر vmBytecodeArrayEncodingKey وأعِده أثناء التشغيل من خلال دالة جلب المفتاح، بحيث يبقى خارج الحزمة، ويُقرأ من تخزين العميل أو يُجلب من الخادم الخلفي.
شاهد
Obfuscator.io Async Executor: Async Bytecode Key Getter and Async-Only VM Virtualization
Obfuscator.io Custom Bytecode Key: Compile-Time Key and Runtime Key Getter (Cookie, Fetch, Decoy Key)
ما الذي تفعله هذه الخيارات
يشفّر vmBytecodeArrayEncoding مصفوفة بايت كود الآلة الافتراضية كي لا تبقى في الناتج
نصًا صريحًا. افتراضيًا يُشتق مفتاح التشفير من البيئة ويُعاد بناؤه على جهة العميل، فلا تتعامل معه أبدًا. هذا مريح، لكن
مادة المفتاح تظل موجودة داخل الحزمة.
يتيح لك خياران إخراج المفتاح من الحزمة والتحكم فيه بنفسك:
vmBytecodeArrayEncodingKey: المفتاح الذي تزوّده وقت البناء. عند ضبطه يُستخدم بدلًا من المفتاح الافتراضي المشتق من البيئة، ولا يُضمَّن في الناتج المشوَّش.vmBytecodeArrayEncodingKeyGetter: تعبير JavaScript يعيد المفتاح نفسه أثناء التشغيل. يُضمَّن حرفيًا ويُقيَّم في المتصفح عند تحميل الكود المشوَّش.
الغاية هي الفصل: لأن المفتاح ليس داخل الكود، لا يستطيع الفحص الساكن البحت للحزمة استرجاعه. لكنه يجب أن يكون حاضرًا أثناء التشغيل كي يعمل الكود، لذا فهو ليس سرًا بالمعنى الحقيقي، غير أنك أنت من يقرر مصدره ومن يُسمح له برؤيته.
هذان الخياران زوج متلازم، والمشوِّش يفرض ذلك: فضبط أحدهما دون الآخر خطأ تحقّق وقت البناء
(vmBytecodeArrayEncodingKey وحده لا يملك وسيلة للحصول على المفتاح أثناء التشغيل، ودالة الجلب وحدها لا تجد مفتاحًا وقت
البناء تتوافق معه). كما أنهما لا يفعلان شيئًا ما لم تكن مصفوفة البايت كود مشفَّرة فعلًا، لذا يجب تفعيل
vmBytecodeArrayEncoding: true أيضًا - أو vmSelfDefending: true، الذي يفرض تفعيل vmBytecodeArrayEncoding
داخليًا. اضبط المفتاحين معًا إلى جانب أحد هذين الخيارين.
كيف يُدمج المفتاحان
لا يُستخدم مفتاحك وحده أبدًا؛ ففي الجانبين يُمزج بمفتاح داخلي يتحكم فيه المشوِّش:
- وقت البناء. يُدمج
vmBytecodeArrayEncodingKeyمع مفتاح داخلي يشتقه المشوِّش، وتُرمَّز مصفوفة البايت كود بالمفتاح الممزوج الناتج. - وقت التشغيل. تُدمج القيمة التي تُرجعها
vmBytecodeArrayEncodingKeyGetterمع المفتاح الداخلي نفسه، الذي يُعاد بناؤه على جهة العميل من عوامل تشغيل مختلفة، لفك ترميز البايت كود.
ولأن الجانبين يمزجان مفتاحك بالمفتاح الداخلي، يجب أن تُرجع دالة الجلب النص نفسه تمامًا الذي مرّرته بوصفه
vmBytecodeArrayEncodingKey. ولا يكفي أي جزء منهما وحده: فمفتاحك دون المفتاح الداخلي لا يستطيع فك ترميز البايت كود،
والمفتاح الداخلي عديم الفائدة دون مفتاحك، ولهذا فإن التحكم في من يتلقى مفتاحك هو ما يحمي الكود فعلًا.
تزويد المفتاح أثناء التشغيل
افتراضيًا تكون دالة الجلب متزامنة: يجب أن يُعيد التعبير المفتاح فورًا عند تحميل الكود المشوَّش. اقرأه من أي
مصدر موجود أصلًا على جهة العميل: ملف تعريف ارتباط، أو localStorage، أو متغير عام، أو عنصر DOM يحقنه الخادم.
يجب أن يكون المفتاح موجودًا قبل تشغيل الكود المشوَّش:
تعمل المصادر المتزامنة الأخرى بالطريقة نفسها، فاختر ما يملؤه تطبيقك بالفعل:
أبقِ المفتاح خارج الملف أو السكربت الذي يحتوي على الكود المشوَّش. فتضمينه هناك يُفسد الغاية كلها، إذ سيسترجع الفحص
الساكن للحزمة الكود ومفتاحه معًا. خزّنه في مصدر منفصل، وحقن vmBytecodeArrayEncodingKey الخاص بوقت البناء من متغير
بيئة أو سرّ بدلًا من إيداعه في المستودع.
جلب المفتاح من الخادم الخلفي (غير متزامن)
يتطلب vmAsyncExecutor · v7.3.0+لا تستطيع دالة الجلب المتزامنة قراءة إلا ما هو موجود أصلًا على جهة العميل. ولجلب المفتاح من خادمك، كي تتمكن من
حمايته خلف المصادقة وإلغائه، يجب أن تكون دالة الجلب غير متزامنة، وهذا يتطلب
vmAsyncExecutor. عند تفعيل المنفِّذ غير المتزامن، يمكن لدالة الجلب أن تُعيد
Promise، وتنتظرها الآلة الافتراضية قبل التشغيل.
يؤدي تفعيل vmAsyncExecutor أيضًا إلى تضييق ما يُحوَّل إلى الآلة الافتراضية: ففي هذا الوضع لا تُحوَّل إلى الآلة الافتراضية
إلا دوال async الأبعد خارجًا، ولا يُحوَّل الكود المتزامن إلى الآلة الافتراضية (لكن بقية التشويش تظل تنطبق عليه). وإذا كان برنامجك متزامنًا في معظمه، فغلّف الكود الذي
تريد حمايته داخل دالة async كي يظل مشمولًا بالحماية - راجع vmAsyncExecutor
للاطلاع على القاعدة الكاملة.
دالة الجلب التي تُعيد Promise تتطلب vmAsyncExecutor. ولا يمكن التحقق من ذلك وقت البناء، لذا فإن دالة جلب تُعيد
Promise مع تعطيل vmAsyncExecutor تفشل أثناء التشغيل.
على الخادم، قرّر أي مفتاح تُعيده بناءً على ما يثق به تطبيقك: جلسة تم التحقق منها، أو فحص ترخيص، وما إلى ذلك. لا
تكفي Origin أو Referer وحدهما لمصادقة المستدعي، وقد تُسقط طلبات GET من الأصل نفسه Origin. والحيلة هنا: بدلًا
من رفض المستدعين غير الموثوقين، أعِد مفتاحًا خاطئًا (VM_DECOY_KEY أدناه). عندها يفشل الكود المحمي من تلقاء نفسه
(أخطاء، أو نتائج خاطئة، أو صفحة تتوقف عن الاستجابة)، وهذا أكثر تخفّيًا من رمز 401 واضح يخبر المهاجم بالضبط بما عليه تجاوزه.
عطّل التخزين المؤقت للاستجابات، كي لا يُقدَّم مفتاح مستدعٍ ما إلى مستدعٍ آخر أبدًا. اربط المفاتيح بإصدار البناء الصحيح، وانشر المفاتيح والحزم معًا. والعميل الذي يتلقى المفتاح الحقيقي لا يزال قادرًا على فحصه أثناء التشغيل.
قدِّم من نقطة النهاية هذه النص نفسه تمامًا الذي مرّرته بوصفه vmBytecodeArrayEncodingKey وقت البناء. تجلب دالة الجلب
أعلاه عنوان URL نسبيًا (/api/vm-key)، لذا فإن نسخة من الحزمة مستضافة على أصل آخر تطلب /api/vm-key
من ذلك الأصل، فلا تتلقى مفتاحك أبدًا؛ أما المفتاح المضلِّل فهو ما يحصل عليه المستدعي عندما يصل فعلًا إلى نقطة
النهاية لكن دون جلسة موثوقة (طلب غير مصادَق عليه، أو حزمة مسروقة يُمرَّر طلبها عبر أصلك). وفي كلتا الحالتين لا يصل المفتاح
الحقيقي أبدًا ولا يعمل الكود المحمي.
ما يُعد "صالحًا" يعتمد كليًا على تطبيقك: جلسة مصادَق عليها، أو ترخيص موقَّع، أو أي مزيج منهما. وأيًّا كان ما يُنتج
المفتاح، غلّفه في Promise وسينتظره المنفِّذ غير المتزامن قبل تشغيل الآلة الافتراضية.
عندما لا يتطابق المفتاح
لا يعمل الكود المشوَّش إلا عندما تُعيد دالة الجلب المفتاح نفسه تمامًا الذي استُخدم أثناء التشويش. فإذا اختلف
المفتاحان، أو أعادت دالة الجلب undefined أو null أو نصًا فارغًا، يفشل الكود أثناء التشغيل: فيُنتج نتائج
خاطئة، أو يُلقي خطأ تشغيل عاديًا، أو يتوقف عن الاستجابة.
لا توجد عمدًا رسالة خطأ مميزة خاصة بالمفتاح: فالمفتاح الفاشل لا يمكن تمييزه عن أي عطل تشغيل آخر. لذا عندما تُلقي حزمة محمية باستخدام VM خطأً، أو تُعيد نتائج خاطئة، أو تتجمد فقط بعد استخدام هذا الخيار، افحص مسار المفتاح أولًا: أن دالة الجلب تُرجع قيمة على الصفحة، وأنها تُعيد نصًا غير فارغ، وأنها تُعيد القيمة نفسها التي بنيت بها.
