كيف عطّل Chrome 142+ سير عمل السكربتات المحلية وكيف أصلحناه

David Négrier
CTO & Founder
تقنية

أدخل Chrome 142 وسائل حماية جديدة ضد هجمات الشبكة المحلية. لمعظم التطبيقات، هذا التغيير بالكاد يُلاحَظ. أما بالنسبة لنا، فقد عطّل سير عمل تطوير محلي محدداً جداً وشائعاً جداً.

إذا كنت تستخدم مجموعة بداية خرائط WorkAdventure وتوقّفت سكربتاتك المخصّصة فجأة عن التحميل من localhost، فأنت لم تُخطئ في أي إعداد. غيّر Chrome طريقة تعامله مع الطلبات المحلية الآتية من إطارات iframe معزولة (sandboxed)، وكان نهجنا السابق يعتمد على ذلك تحديداً.

في هذا المقال، سأشرح ما الذي تغيّر، ولماذا يفشل (حتى مع ترويسات CORS متساهلة)، والحل المؤقت الذي أطلقناه، والإصلاح طويل الأمد الذي يجعل التطوير والإنتاج يتصرّفان بالطريقة نفسها.

إذا كنت تريد الإصلاح فقط

الإصلاح مُطلَق بالفعل في WorkAdventure v1.27.6. يفتح هذا الإصلاح إمكانيات أكثر للسكربتات المحلية، وإذا أردت الاستفادة من هذه الإمكانيات عندما يكون سكربتك في الإنتاج، فننصحك بما يلي:

حدّث إضافة Vite في ملف package.json الخاص بمجموعة بداية الخرائط لديك:

"wa-map-optimizer-vite": "^1.2"

ثم شغّل npm install مرة أخرى.

ستُحمَّل سكربتاتك الآن من أصل (origin) سليم في كلٍّ من التطوير والإنتاج، ما يجعل سلوك CORS أيضاً أكثر قابلية للتوقّع بكثير.

يشرح باقي المقال ما حدث ولماذا يُصلحه هذا.

ما الذي تغيّر في Chrome 142

أدخل Chrome 142 فعلياً تغييرين مهمّين هنا.

1) طلب إذن جديد لـ «الوصول إلى الشبكة المحلية»

عندما تحاول صفحة الوصول إلى موارد على الشبكة المحلية، مثل localhost، يطلب Chrome الآن قراراً صريحاً من المستخدم.

في حالتنا، عندما يحاول WorkAdventure (المستضاف على https://play.workadventu.re) تحميل موارد من خادم التطوير المحلي لديك على localhost:5173، يعرض Chrome طلباً يسأل ما إذا كان يجب السماح بالوصول إلى الشبكة المحلية أو رفضه.

طلب إذن Chrome للوصول إلى الشبكة المحلية لطلبات localhost (السماح أو الرفض)

هذا الجزء يمكن التعامل معه. تنقر على «السماح» وتتابع.

2) التغيير الكاسر: الطلبات المحلية من أصول مبهمة محجوبة

التغيير الأكثر إرباكاً هو طريقة تعامل Chrome مع طلبات الشبكة المحلية الآتية من أصل يعتبره غير موثوق.

تنتهي إطارات iframe المعزولة المُنشأة دون allow-same-origin بأصل مبهم (opaque origin). عملياً، يظهر ذلك غالباً كـ origin: null. ومع Chrome 142، تُحجب طلبات الشبكة المحلية الآتية من null في مرحلة مبكرة جداً، قبل حتى تقييم CORS.

هذه هي التفصيلة الأساسية: حتى لو أرسل خادم التطوير لديك ترويسات CORS متساهلة، فقد يحجب Chrome الطلب قبل أن تكون لتلك الترويسات أي أهمية.

لماذا عطّل هذا سير عمل مجموعة بداية الخرائط

صُمّمت مجموعة بداية خرائط WorkAdventure للتكرار السريع.

عندما تشغّل:

$ npm run dev

يبدأ خادم تطوير على localhost:5173. يحمّل WorkAdventure بعد ذلك الخرائط والصور وJavaScript وCSS وموارد أخرى من ذلك الخادم، فترى التغييرات في الوقت الفعلي.

بالنسبة لموارد الخرائط، يكفي عادةً طلب إذن Chrome الجديد للمتابعة. لكن السكربتات حالة خاصة في WorkAdventure.

تعمل السكربتات في إطارات iframe معزولة بحكم التصميم

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

قبل Chrome 142، كان مسار التطوير المحلي لدينا كالتالي:

  1. ينشئ WorkAdventure إطار iframe فارغاً بـ sandbox=“allow-scripts” (دون allow-same-origin)

  2. يستخدم srcdoc لحقن مستند HTML صغير في ذلك الإطار

  3. يتضمّن ذلك المستند وسم <script> يشير إلى خادم التطوير لديك، مثلاً http://localhost:5173/my-script.js

  4. يجلب المتصفح السكربت من localhost وينفّذه داخل الإطار المعزول

مخطط تسلسلي: Chrome 142+ يحجب سكربت localhost من إطار iframe معزول (الأصل null)

بعد Chrome 142، تفشل الخطوة 4.

ما الذي ستراه عند الفشل

عند حجب السكربت، لا يُحمَّل أبداً، ويسجّل Chrome أخطاء مثل:

Access to script at ‘http://localhost:5173/src/main.ts ’ from origin ‘null’ has been blocked by CORS policy: Permission was denied for this request to access the unknown address space.

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

الإصلاح الأول: فكّ حظر التطوير المحلي بنقطة نهاية وكيلة

لإزالة العائق عن المطوّرين بسرعة، غيّرنا طريقة تحميل WorkAdventure لسكربتات المستخدمين خلال التطوير المحلي.

يعرض خادم WorkAdventure الآن نقطة نهاية وكيلة:

/local-script?script=XXX

بدلاً من ترك الإطار المعزول يجلب السكربت مباشرة من localhost، يجلبه WorkAdventure عبر هذه النقطة. يجعل هذا الطلب صادراً من أصل خادم WorkAdventure، وليس من null.

«أليست هذه مشكلة أمنية؟»

قد تكون كذلك، حسب طريقة التنفيذ. لقد حددنا نطاقها بدقة.

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

أصلح هذا الحل عطل Chrome 142 المباشر. لكنه أدخل مشكلة أخرى.

العيب الخفي: لم يعد التطوير والإنتاج متطابقين

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

في التطوير، صارت السكربتات تُحمَّل من إطار iframe أصله https://play.workadventu.re (خادم WorkAdventure). لكن في الإنتاج، كانت السكربتات لا تزال تُحمَّل باستخدام srcdoc، فبقي أصل الإطار null.

قد يسبّب هذا التباين مشكلات يصعب تشخيصها.

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

لذا قرّرنا التقدّم أكثر وإصلاح التباين الجوهري.

الإصلاح طويل الأمد: إعطاء السكربتات أصلاً حقيقياً في كل مكان

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

لم يكن استخدام /local-script في الإنتاج خياراً. فوكيل إنتاج يمكنه تقديم سكربتات عشوائية مستضافة على WorkAdventure لكود المستخدم سيكون خطراً أمنياً كبيراً، مع إمكانية الوصول إلى واجهات برمجية داخلية وبيانات المستخدمين.

بدلاً من ذلك، اعتمدنا على شيء صحيح بالفعل في خرائط WorkAdventure.

أين تُستضاف الخرائط اليوم

تُستضاف خرائط WorkAdventure إما:

  • على GitHub Pages، للإعدادات القديمة
  • أو على خادم تخزين خرائط WorkAdventure

أعطانا هذا مساراً نظيفاً: استضافة صفحة HTML بجوار السكربت، وتحميل تلك الصفحة في الإطار باستخدام عنوان src عادي.

لماذا هذا آمن بما يكفي

في عرض SaaS، يستخدم خادم تخزين الخرائط نطاقاً واحداً لكل عالم. يعزل هذا السكربتات لكل عالم عميل على مستوى النطاق.

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

ما الذي غيّرناه في مجموعة بداية الخرائط

تأتي مجموعة بداية الخرائط مع إضافة Vite مسؤولة عن بناء السكربتات. وسّعنا تلك الإضافة لتولّد أيضاً صفحة HTML صغيرة لكل سكربت، تحمّل السكربت عبر وسم <script>.

مثلاً، إذا كان لديك سكربت باسم my-script.js، تولّد الإضافة my-script.html كالتالي:

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://play.workadventu.re/iframe_api.js"></script>
    <script src="my-script.js"></script>
</head>
<body>
</body>
</html>

يحمّل WorkAdventure بعد ذلك صفحة HTML في الإطار باستخدام src، بدلاً من حقن srcdoc ومحاولة جلب السكربت من localhost مباشرة.

النتيجة هي بالضبط ما أردناه:

  • أصل سليم في التطوير
  • أصل سليم في الإنتاج
  • سلوك متّسق عبر البيئات

وكمكافأة، صارت السكربتات التي كانت تعاني سابقاً مع الموارد المحمية بـ CORS أسهل فهماً بكثير الآن.

الخلاصة

لم يُضِف Chrome 142 مجرد طلب إذن. بل غيّر أيضاً طريقة التعامل مع طلبات الشبكة المحلية من الأصول المبهمة، مثل إطارات iframe المعزولة بـ origin: null. عطّل ذلك نمطاً شائعاً لتحميل السكربتات المحلية خلال التطوير.

أطلقنا حلاً مؤقتاً قصير الأمد لإزالة العائق عن التطوير المحلي، ثم نفّذنا نهجاً أكثر متانة: تحميل السكربتات عبر صفحات HTML مخصّصة مستضافة بجوار الخرائط، ليكون للسكربتات أصل حقيقي في كلٍّ من التطوير والإنتاج.

إذا كنت تستخدم مجموعة بداية الخرائط، فترقية wa-map-optimizer-vite إلى ^1.2 ستُعيدك إلى سير عمل سلس، مع مفاجآت أقل في الأصل وCORS على طول الطريق.