التوثيق
/
وصفات عملية
/

استبدال القوالب من جهة الخادم

استبدال القوالب من جهة الخادم

Pro
v6.10.0+

أدرج قيمًا يولّدها الخادم (مثل العناصر النائبة `{{ .Field }}` على طريقة قوالب Go) داخل JavaScript المشوَّش باستخدام VM دون كسر خط التشويش.

المشكلة

يقدّم خادمك الخلفي (Go أو Rails أو Django أو PHP أو غيرها) ملف JavaScript يجب توليد جزء من محتواه لكل طلب على حدة: نقطة نهاية API، أو قائمة بأعلام الميزات، أو كتلة الحالة الأولية، أو معرِّف البناء، أو قيمة nonce. أنت تشوّش الكود باستخدام vmObfuscation: true، لكنك تحتاج أيضًا إلى أن يستبدل محرك القوالب العناصر النائبة في الناتج المشوَّش بعد انتهاء التشويش. يُبقي reservedNames وreservedStrings العنصر النائب ظاهرًا في الناتج كي يمكن استبداله. ومن دونهما، يُبتلَع العنصر النائب النصي داخل بايت كود الآلة الافتراضية، ويُخرَج العنصر النائب على شكل معرِّف في عدة مواضع مُعاد كتابتها، فإما ألا يجد محرك القوالب ما يستبدله، وإما أن يُفسد الناتج.

فكّر في تحميل القيم كبيانات بدلًا من ذلك

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

JavaScript

استخدم الأنماط أدناه عندما يتحتم فعلًا استبدال القيم داخل الملف المشوَّش.

ملاحظة حول أمثلة Go: تُجري هذه الأمثلة استبدالًا نصيًا بسيطًا (strings.ReplaceAll / strings.NewReplacer) على الناتج المشوَّش وتتولى التهريب بنفسها، كما هو موضح في كل نمط. وهي لا تمر عبر الحزمة html/template في Go، إذ إن ذلك سيطبّق تهريب JavaScript السياقي الخاص بها فوق ذلك، فتُهرَّب الحمولة مرتين. وإذا كنت تعرض القالب عبر html/template فعلًا، فاحذف التهريب اليدوي ودع المحرك يهرّب مرة واحدة.

أي خيار أختار؟

قيمة الخادم هي…استخدم
قيمة JS خام (مصفوفة، أو كائن، أو رقم، أو قيمة منطقية - أي قيمة JSON)النمط 1 - reservedNames + عنصر نائب على شكل معرِّف
نص (الحالة الشائعة - JSON مولَّد، أو معرِّف بناء، أو قيمة nonce)النمط 2 - reservedStrings + عنصر نائب على شكل قالب حرفي
نص، حيث يجب أن يبقى الكود بمعيار ES5 أو تحتاج واجهة API المحيطة إلى نص حرفي بين علامتي اقتباسالنمط 3 - reservedStrings + عنصر نائب على شكل نص بين علامتي اقتباس

الاستبدال بعد التشويش غير متوافق مع vmSelfDefending: true. راجع ملاحظات التوافق في نهاية هذه الوصفة.

النمط 1 - reservedNames مع عنصر نائب على شكل معرِّف

الاستخدام الأنسب: إدراج تعبيرات JS خام (مصفوفات، كائنات، أرقام، وغيرها) دون أي قلق بشأن تهريب علامات الاقتباس.

اختر معرِّفًا مميزًا لن يتعارض أبدًا مع الكود الحقيقي، وأشِر إليه مباشرة، وأضف تعبيرًا نمطيًا يطابقه إلى reservedNames. في ظل تشويش VM، يُمرَّر المعرِّف عبر مصفوفة التعبيرات المحجوزة ويظهر حرفيًا في الناتج، مثلًا:

JavaScript

استبدل المعرِّف بتعبير كامل متسلسل بصيغة JSON. لا تضع القيمة داخل علامات اقتباس أو علامات backtick، فالمعرِّف ليس داخل نص حرفي، ولذلك يحلّل محرك JS القيمة المُدرجة كتعبير عادي. استخدم أداة تسلسل موثوقة، وهرِّب < عندما يكون السكربت مضمَّنًا في HTML، واختبر قيمًا تحتوي على علامات اقتباس وشرطات مائلة عكسية وفواصل أسطر وعلامات backtick و${...}.

JavaScript

النمط 2 - reservedStrings مع عنصر نائب على شكل قالب حرفي

الاستخدام الأنسب: محركات القوالب على طريقة Go أو Jinja التي تتطلب محددات مثل {{ .Field }}، والتي تصادف أنها نص JS صالح عند إحاطتها بعلامات backtick.

يوجد العنصر النائب داخل قالب حرفي بجزء نصي واحد ودون استيفاء. في ظل تشويش VM يُمرَّر عبر مصفوفة التعبيرات المحجوزة، فيُحفظ شكل backtick الخام في الناتج.

المصدر

JavaScript

خيارات المشوِّش

واجهة المستخدم

لحجز العنصر النائب {{.Config.FeatureFlags}} من المصدر أعلاه، أضف هذا التعبير النمطي إلى حقل Reserved Strings بشرطات مائلة عكسية مفردة. تخزّن الواجهة القيمة حرفيًا، لذا لا تُضاعَف الشرطات المائلة العكسية بخلاف ما يحدث في كود JS:

نص

حقل Reserved Strings في واجهة المشوِّش وقد أُدخل فيه التعبير النمطي بشرطات مائلة عكسية مفردة

واجهة API

JavaScript

بعد التشويش

JavaScript

يُحفظ العنصر النائب بايتًا ببايت، بما في ذلك علامات backtick المحيطة به.

الاستبدال من جهة الخادم

استبدل {{.Config.FeatureFlags}} بنص JSON مهرَّب ليناسب قالبًا حرفيًا. لا تتطلب علامات backtick تهريب " الداخلية، لكن الشرطة المائلة العكسية أو علامة backtick أو ${ داخل الحمولة ستظل تغيّر القالب الحرفي أو تُنهيه، لذا هرِّب هذه الثلاثة:

الكود

أثناء التشغيل: JSON.parse(`{"newCheckout":true,"darkMode":false}`) يعمل.

لماذا تُفضَّل القوالب الحرفية على علامات الاقتباس المفردة أو المزدوجة لقيم النصوص؟ لا تتطلب علامات backtick تهريب " داخل حمولة JSON. وهذا مهم لأن معظم القيم التي يولّدها الخادم هي JSON، وJSON مليء بعلامات الاقتباس المزدوجة. مع عنصر نائب بين علامتي اقتباس مزدوجتين ستحتاج إلى تهريب كل " داخلية (انظر النمط 3)؛ أما مع علامات backtick فلا تحتاج الحمولة إلا إلى تهريب ما ندر فيها من شرطات مائلة عكسية وعلامات backtick و${.

النمط 3 - reservedStrings مع عنصر نائب على شكل نص بين علامتي اقتباس

الاستخدام الأنسب: الكود المصدري الذي يجب أن يبقى بمعيار ES5 (بلا قوالب حرفية)، أو الحالات التي تتوقع فيها واجهة API المحيطة نصًا حرفيًا عاديًا.

العنصر النائب نص حرفي بين علامتي اقتباس مفردتين أو مزدوجتين. في ظل تشويش VM يُمرَّر عبر مصفوفة النصوص المحجوزة، التي تُخرَج كمصفوفة JS متسلسلة عبر JSON.stringify، وتكون دائمًا بعلامات اقتباس مزدوجة أيًّا كان نمط الاقتباس في المدخلات.

المصدر

JavaScript

خيارات المشوِّش

واجهة المستخدم

لحجز العنصر النائب {{.Page.Tags}} من المصدر أعلاه، أضف هذا التعبير النمطي إلى حقل Reserved Strings بشرطات مائلة عكسية مفردة. تخزّن الواجهة القيمة حرفيًا، لذا لا تُضاعَف الشرطات المائلة العكسية بخلاف ما يحدث في كود JS:

نص

حقل Reserved Strings في واجهة المشوِّش وقد أُدخل فيه التعبير النمطي بشرطات مائلة عكسية مفردة

واجهة API

JavaScript

بعد التشويش

JavaScript

الاستبدال من جهة الخادم

لأن العنصر النائب يقع داخل نص JS بين علامتي اقتباس مزدوجتين، يجب تهريب الشرطات المائلة العكسية وأحرف " الداخلية في حمولة JSON المُدرجة:

الكود

الناتج:

JavaScript

أثناء التشغيل: JSON.parse("[\"news\",\"tech\",\"release\"]") ← ["news","tech","release"].

إذا نسيت التهريب، فسيرى المتصفح علامات اقتباس غير متوازنة ويرمي SyntaxError. أما النمط 2 فيتجنب علامات الاقتباس المزدوجة كليًا باستخدام علامات backtick.

عدة عناصر نائبة في برنامج واحد

يمكن الجمع بين الأنماط الثلاثة. فتعبير نمطي واحد في reservedStrings يستخدم البدائل يستطيع مطابقة كل أشكال العناصر النائبة التي يُخرجها محرك القوالب لديك - وهنا كلا النمطين {{ .Field }} و%{ .Field }:

JavaScript

يمكنك المزج بين العناصر النائبة على شكل معرِّفات (لقيم JS الخام) والعناصر النائبة على شكل نصوص (لبيانات JSON المولَّدة) في المصدر نفسه، واختيار النوع لكل عنصر نائب بحسب ما سيُدرجه الخادم فعلًا.

ملاحظات التوافق

يكسر vmSelfDefending الاستبدال بعد التشويش

يكتشف خيار VM Self Defending أي تغيير على الناتج المشوَّش بعد بنائه، بما في ذلك استبدال القوالب المشروع، فيرفض الكود المحمي حينها العمل.

إذا كنت تعتمد على استبدال القوالب من جهة الخادم، فاضبط vmSelfDefending: false. واترك selfDefending معطَّلًا كذلك: فهو بلا أثر في ظل تشويش VM، ومن دون VM يمنع أيضًا أي تغيير على الناتج.

نصائح حول العناصر النائبة

  • لا تُعِد استخدام العناصر النائبة نفسها للمعرِّفات والنصوص. الاسم المحجوز مثل __TOKEN__ والنص المحجوز الذي يطابق __TOKEN__ يصفان مسارين مختلفين في الكود (مصفوفة التعبيرات المحجوزة مقابل مصفوفة النصوص المحجوزة). استخدم أشكالًا نصية مختلفة لكل منهما، مثل اصطلاح UPPER_SNAKE للعناصر النائبة على شكل معرِّفات، وشكل محاط بمحددات ({{ ... }} أو %{...} أو <<<...>>>) للعناصر النائبة على شكل نصوص. وبهذا لا يمكن لخطأ في أحد التعبيرين النمطيين أن يطابق الآخر بصمت.
  • تُطبَّق تعبيرات reservedStrings النمطية على قيم النصوص الخام. يُطابَق التعبير النمطي مع قيمة النص أثناء التشغيل، لا مع النص المصدري. فالتعبير \{\{[^}]+\}\} يطابق النصوص التي تحتوي على {{.something}} (أو أي شكل آخر من {{...}}). وإذا كان العنصر النائب قد يُحاط بمحتوى إضافي ("prefix-{{.Field}}-suffix")، فسيظل التعبير النمطي يطابقه لكن النص بأكمله هو ما يُحفظ، فخطّط للاستبدال على هذا الأساس.
  • يعمل مع أي محرك قوالب. رغم أن الأمثلة تستخدم صياغة Go، فلا شيء في التكامل مع javascript-obfuscator خاص بلغة Go. أي أداة قادرة على إجراء استبدال على مستوى النص في ناتج المشوِّش ستعمل: Rails ERB أو Django أو وسوم PHP المختصرة أو sed في خط CI، وغيرها. اختر محددات يُخرجها محركك بشكل طبيعي ولا تتعارض مع صياغة JS الحقيقية.