Caputchin
فهم Caputchin

كيف يعزل Caputchin الألعاب

لعبة Caputchin كود طرف ثالث يعمل في متصفّح زائرك، على صفحتك. سواء جاءت من Marketplace أو استضفتها بنفسك، يعاملها Caputchin كغير موثوقة افتراضيًّا ويلفّها في عدّة طبقات عزل مستقلّة، فتستطيع لعبة معطوبة أو معادية أن تُعرَض وتُلعَب وتبلّغ نتيجةً، لكنها لا تبلغ شيئًا آخر: لا صفحتك، ولا بيانات زائرك، ولا الشبكة.

تشرح هذه الصفحة كل تدبير، ولماذا يوجد كلٌّ، وكيف يتفاعل الأمر كله مع CSP تضيفه إلى موقعك أنت.

نموذج التهديد في سطر

تشغّل اللعبة JavaScript عشوائيًّا (وربما WebAssembly) يقدّمها طرف ثالث. هدف العزل إذن: تستطيع اللعبة الحوسبة والرسم، وتستطيع تسليم نتيجة إلى الأداة، لكنها لا تستطيع قراءة أو التأثير في أي شيء خارج إطارها. كل طبقة أدناه تخدم ذلك الهدف الواحد، وهي دفاع بالعمق، لا طبقة واحدة موثوقة بأنها مثالية.

الطبقة 1: إطار iframe معزول بأصل معتم

تعمل كل لعبة داخل <iframe> تبنيه الأداة بسمة sandbox دنيا عمدًا. الرمز الوحيد الممنوح هو allow-scripts، لأن اللعبة JavaScript ويجب أن تعمل. كل ما عداه محجوب، وأهمّ حذف هو allow-same-origin.

بدون allow-same-origin يعطي المتصفّح الإطار أصل null فريدًا. تلك الحقيقة الواحدة هي الحدّ الحامل:

  • لا تستطيع اللعبة قراءة DOM صفحتك، أو كوكيزها، أو localStorage، أو جلسة Caputchin، فهي مختومة في أصل قابل للرمي غريب عن أصلك.
  • ولأن الإطار لا يستطيع أيضًا التنقّل بنافذتك العليا، أو فتح نوافذ منبثقة، أو إرسال نماذج، أو إظهار حوارات أصلية (كل تلك القدرات رموز عزل لا يمنحها Caputchin)، فلا مسار من اللعبة عودةً إلى صفحتك.

تركيبة allow-scripts بدون allow-same-origin هي بيت القصيد كلّه: ينفّذ المتصفّح كود اللعبة لكنه يعامل الإطار كأصل غريب لا يلمس شيئًا من أصلك. لا يضيف Caputchin أبدًا allow-same-origin إلى إطار لعبة. (وكنتيجة، كل رسالة ترسلها اللعبة إلى الأداة تصل من أصل "null"، وهو ما تتوقّعه فحوص قناة الأداة، فأصل غير null سيشير بنفسه إلى عزل مُعَدّ خطأً.)

الطبقة 2: وثيقة الإطار تُبنى مضمَّنةً، لا تُجلَب

لا توجّه الأداة الإطار إلى صفحة بعيدة. تبني وثيقة HTML الكاملة للإطار مضمَّنةً (عبر srcdoc) وتسلّمها للإطار ذي الأصل null. تلك الوثيقة ضئيلة: وسم CSP صارم في meta (الطبقة التالية)، وإقلاع زمن تشغيل Caputchin صغير، ووسم <script> واحد يحمّل حزمة اللعبة.

هذه الآلية نفسها لمساري التسليم كليهما، ولا يختلف سوى رابط الحزمة:

مصدر اللعبةرابط الحزمة في الوثيقة المضمَّنة
Marketplace (محلولة من المنصّة)رابط الحزمة المثبَّت والمجزَّأ بالسلامة الذي حلّه Caputchin عند التركيب.
مستضافة ذاتيًّا (game-src خاصتك)الرابط الذي قدّمته.

ولأن الوثيقة مؤلَّفة من الأداة لا اللعبة، لا تتحكّم اللعبة أبدًا بالـ CSP، أو زمن التشغيل، أو بنية الإطار، بل فقط بما تفعله حزمتها بعد تحميلها.

الطبقة 3: Subresource Integrity لألعاب Marketplace

حين يقدّم Caputchin لعبة Marketplace يثبّت الحزمة على نسخة غير قابلة للتغيير ويسجّل تجزئة تعمّية (SHA-384) منها. وسم <script> الذي يحمّل الحزمة يحمل تلك التجزئة كسمة integrity مع crossorigin="anonymous"، فيرفض المتصفّح نفسه تنفيذ الحزمة إن اختلف بايت واحد عمّا ثُبِّت. CDN مخترَق لا يستطيع استبدال كود مختلف؛ يفشل التحميل مغلقًا.

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

الطبقة 4: Content-Security-Policy مضمَّنة صارمة

تحمل الوثيقة المضمَّنة Content-Security-Policy ضيّقةً. رغم أن الإطار أصلًا بأصل null ومعزول، يقيّد الـ CSP أكثر ما يستطيع الكود المحمَّل فعله، يرفض كل شيء افتراضيًّا ويعيد منح الحدّ الأدنى فقط:

التوجيهالقيمةلماذا
default-src'none'ارفض كل ما لا يُسمَح به صراحةً أدناه.
script-srcتجزئة لزمن تشغيل Caputchin المضمَّن + أصل حزمة اللعبة + 'wasm-unsafe-eval'شغّل فقط إقلاع Caputchin بالضبط (مثبَّتًا بتجزئة sha256-، لا 'unsafe-inline') وحزمة اللعبة نفسها. 'wasm-unsafe-eval' يتيح لمحرّك WASM التصريف دون منح 'unsafe-eval' الكامل.
connect-src'none'توجيه مكافحة التسريب. لا تستطيع اللعبة fetch، أو XHR، أو فتح WebSocket، أو الإرسال إلى أي مكان. لا خروج شبكة البتّة.
img-srcdata: (إضافةً إلى أي أصول مظهر، أدناه)صور نقطية مضمَّنة، أو من مضيف أصل مسموح.
media-srcdata: (إضافةً إلى أي أصول مظهر، أدناه)صوت/فيديو مضمَّن، أو من مضيف أصل مسموح.
font-srcdata:خطوط مضمَّنة فقط.
style-src'unsafe-inline'الألعاب تضبط أنماطًا مضمَّنة؛ لا أنساق تنسيق خارجية. (الأنماط لا تستطيع تسريب بيانات كما يفعل السكربت أو الشبكة.)

الأثر الصافي: تعمل اللعبة، وتُعرَض، وتبلّغ نتيجتها عبر جسر الـ SDK، ولا تستطيع بلوغ شيء آخر، خصوصًا لا الشبكة. هذا الـ CSP يضبطه Caputchin وليس شيئًا تعدّه؛ مذكور هنا كي تفهم كم اللعبة محصورة بالضبط.

المكان الوحيد الذي يوسّع فيه إعداد عميل CSP الإطار

تستطيع المظاهر توجيه حقول الصور أو الصوت إلى رابط مطلق على CDN خاصتك (انظر المظاهر). كي تُحمَّل تلك الموارد داخل الإطار المحكَم، يضيف Caputchin أصول تلك الموارد بالضبط فقط إلى img-src / media-src للإطار، ولا شيء سواها. script-src وconnect-src لا يُوسَّعان أبدًا، فحتى أصل مورد مسموح لا يمكن استخدامه لتحميل كود أو فتح قناة شبكة. هذه الطريقة الوحيدة الضيّقة التي تؤثّر بها قيمة مُعَدّة في سياسة الإطار، وهي ما زالت للموارد فقط.

الطبقة 5: قناة نتيجة باتّجاه واحد

ليس للّعبة سطح قابل للنداء إلى صفحتك. المخرج الوحيد جسر الـ SDK: تنادي اللعبة طريقة واحدة تُرسِل بـ postMessage نتيجتها المعتمة (أثرها) إلى الأداة، التي تعيش على أصلك. الأداة هي ما يكلّم API Caputchin؛ واللعبة لا تفعل أبدًا (لا تستطيع، connect-src هو 'none'). فمسار الثقة لعبة ← أداة ← خادمك الخلفي، وكل قفزة تمرّر نتيجة إلى الأمام فقط، لا تمنح اللعبة بلوغًا إلى الوراء أبدًا.

الـ CSP الذي تضبطه على صفحتك

الطبقات أعلاه تحمي أنت وزائرك من اللعبة. Content-Security-Policy على صفحتك أنت ضابط مختلف ومكمّل: يحمي صفحتك من كل شيء.

حقيقة واحدة تحكم كل ما في هذا القسم: إطار اللعبة يرث سياسة صفحتك. الإطار وثيقة srcdoc مضمَّنة، والمتصفّحات تطبّق على مثل هذه الوثائق سياسة الصفحة المُضمِّنة فوق سياسة الإطار نفسه. فسياسة الإطار المحكمة المعروضة أعلاه أرضيةٌ يضيفها Caputchin، لا بديلٌ عن سياستك. كل ما يعمل داخل الإطار عليه أن يرضي الاثنتين معًا، ومعنى ذلك أن سياسة صفحتك عليها أن تسمح بحزمة اللعبة وبسكربت الإقلاع الخاص بالإطار، مع أن صفحتك لا تحمّل أيًّا منهما مباشرةً.

كحدّ أدنى، تحتاج الأداة إلى:

التوجيهاسمحلماذا
script-srcالأصل الذي تحمّل منه سكربت الأداة، إضافةً إلى 'unsafe-eval'الـ <script> الذي يعرّف <caputchin-widget> / <caputchin-game>، والـ CDN الذي اخترته (jsDelivr، أو مضيفك الخاص، أو caputchin.com). 'unsafe-eval' يتيح تشغيل تحدّي الرصد؛ وهو مطلوب ما دام الرصد مفعّلًا، وتستطيع إسقاطه بإطفاء الرصد على صفحة الأمان للمفتاح.
script-src (للألعاب فقط)أصل حزمة اللعبة، إضافةً إلى 'unsafe-inline' و'wasm-unsafe-eval'لأن الإطار يرث هذه السياسة. أصل الحزمة هو https://games.caputchin.com لألعاب Marketplace، أو مضيفك الخاص / الـ CDN خاصتك إن كانت لعبة مستضافة ذاتيًّا. و'unsafe-inline' يغطّي سكربت الإقلاع الخاص بالإطار، وهو سكربت يثبّته الإطار ببصمة sha256- ولا تستطيع صفحتك تثبيته، لأن تلك البصمة تتغيّر مع كل إصدار من الأداة. و'wasm-unsafe-eval' تحتاجه الألعاب المبنية على محرّك WebAssembly. ولا ينطبق شيء من هذا إن كنت تستخدم <caputchin-widget> وحدها بلا لعبة.
connect-srchttps://verify.caputchin.com، إضافةً إلى الأصل الذي تحمّل منه سكربت الأداةنداءان منفصلان. تنادي الأداة خدمة التحقّق من Caputchin لإعداد تحقّق وتأكيده، فاسمح بهذا الأصل بالضبط (وإن وجّهت الأداة إلى مضيف API مختلف، فاسمح بذلك بدلًا). ويجلب حلّال proof-of-work فوق ذلك وحدة WebAssembly خاصته من جوار سكربت الأداة، فأصل الـ CDN مكانه هنا كما هو في script-src. وإن استضفت الأداة على أصلك الخاص، غطّى 'self' الثاني.
worker-srcblob:يعمل حلّال proof-of-work كليًّا في Web Workers منشأة من رابط blob:، بلا ارتداد للخيط الرئيسي، فهذا مطلوب. إن أسقطت worker-src، ترتدّ المتصفّحات إلى child-src ثم default-src، وdefault-src 'self' وحده لا يسمح بـ blob:.

أضف 'wasm-unsafe-eval' إلى script-src ليعمل حلّال proof-of-work كـ WebAssembly سريع. لا هذه الكلمة ولا الأصل الذي يجلب منه الحلّال مطلوبٌ لينجح التحقّق: فإن غاب أيٌّ منهما، ارتدّ الحلّال إلى تنفيذ JavaScript خالص أبطأ (ما زال داخل العامل) وظلّ التحقّق عاملًا. لكنهما لا يثمران إلا معًا. يجلب الحلّال وحدة WebAssembly بـ fetch ثم يصرّفها، فالمسار السريع يحتاج أصل سكربت الأداة في connect-src و'wasm-unsafe-eval' في script-src؛ والسماح بأحدهما دون الآخر يتركك على الحلّال البطيء دون شيء في الطرفية يقول لماذا.

لا تحتاج إلى مدخل frame-src للعبة. إطار اللعبة وثيقة srcdoc مضمَّنة لا وثيقة مجلوبة من رابط، فلا رابط أصلًا ليطابقه frame-src، ويُحمَّل الإطار حتى تحت frame-src 'none'.

أمّا ما تحتاجه فعلًا، وهو ما يفاجئ الناس، فهو أصل حزمة اللعبة في script-src صفحتك، مع أن صفحتك لا تحمّل تلك الحزمة أبدًا: الإطار هو الذي يحمّلها، والإطار يعمل تحت سياستك إلى جانب سياسته. والأمر نفسه ينطبق على سكربت إقلاع الإطار وعلى محرّك لعبة بـ WebAssembly. ومنحها لا يوسّع ما تبلغه اللعبة: فسياسة الإطار نفسه ما زالت تثبّت connect-src 'none'، وما زال الإطار بأصل معتم، ولا يستطيع ما تمنحه صفحتك أن يرخي أرضيةً يفرضها الإطار على نفسه.

'unsafe-inline' في script-src هو الجزء المزعج من تلك القائمة، ومن الأمانة قول ذلك: فهو مطلوب اليوم لتشغيل لعبة، ويضعف حماية صفحتك نفسها من السكربتات المضمَّنة، والبديل (تثبيت بصمة sha256- لسكربت إقلاع الإطار) غير عملي ما دامت تلك البصمة تتحرّك مع كل إصدار من الأداة. وإن لم تقبل هذه المقايضة في صفحة بعينها، فاستخدم فيها <caputchin-widget>، فهي لا تحتاج شيئًا من صفّ الألعاب.

إن فشل عنصر Caputchin بصمت في التحميل تحت CSP خاصتك، تسمّي طرفية متصفّحك التوجيه المحجوب؛ أضف الأصل أو الكلمة التي تسمّيها إلى ذلك التوجيه. ابدأ صارمًا وافتح بالضبط ما تطلبه الطرفية، واقرأ المخالفات المسجّلة على about:srcdoc بالعناية نفسها التي تقرأ بها مخالفات صفحتك: تلك هي إطار اللعبة وهو يصطدم بسياستك الموروثة، وهي التي يفوّتها الناس. تحتاج إلى blob: للعمّال (worker-src)، وما دام الرصد مفعّلًا، إلى 'unsafe-eval' في script-src؛ ويزول متطلّب 'unsafe-eval' إن أطفأت الرصد على صفحة الأمان للمفتاح. ولا تحتاج 'unsafe-inline' إلا لتشغيل الألعاب، بحسب الجدول أعلاه. أمّا المسار السريع لـ proof-of-work فاختياري لكنه مزدوج: 'wasm-unsafe-eval' في script-src وأصل سكربت الأداة في connect-src. وهذا الزوج هو الحالة الوحيدة التي لن تعينك فيها الطرفية، لأن الارتداد إلى حلّال JavaScript ليس خطأً.

سياسة تعمل

تحميل الأداة من jsDelivr، مع ألعاب Marketplace، والرصد مفعّل:

default-src 'none';
script-src  https://cdn.jsdelivr.net https://games.caputchin.com 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval';
connect-src https://verify.caputchin.com https://cdn.jsdelivr.net;
worker-src  blob:;
img-src     'self' data: https:;
style-src   'unsafe-inline'

أسقط https://games.caputchin.com و'unsafe-inline' و'wasm-unsafe-eval' إن لم تستخدم الألعاب. وأسقط 'unsafe-eval' إن أطفأت الرصد. واستبدل https://cdn.jsdelivr.net بأصلك الخاص في script-src وconnect-src معًا إن استضفت الأداة بنفسك.

لماذا كل هذه الطبقات

كل طبقة ستكون حدًّا ذا معنى وحدها؛ ومعًا تعني أن فشل واحدة ليس اختراقًا. الأصل null وحده يعزل اللعبة عن بياناتك أصلًا؛ والـ CSP وحده يقتل خروج الشبكة أصلًا؛ والسلامة وحدها تثبّت البايتات بالضبط أصلًا. يشغّل Caputchin جميعها لأن كود الطرف الثالث غير الموثوق هو بالضبط المكان الذي يستحقّ فيه الدفاع بالعمق أجره.

انظر أيضًا

في هذه الصفحة