الأمان
نموذج أماننا في جملة واحدة: لا يوجد شيء على الخادم يمكن مهاجمته. كل ما يلي قابل للتحقق من DevTools لديك.
البنية التقنية
المولّد تطبيق صفحة واحدة ثابت يُقدَّم من Cloudflare Pages: لا حسابات مستخدمين، ولا مصادقة، ولا أي مسار كود خلفي في أي مكان في تدفق التوليد. كل عملية توليد وتشفير وفحص وعرض لرمز QR تعمل بالكامل داخل متصفحك. توجد إلى جانبه نقطة نهاية واحدة صغيرة بلا خادم دائم، وهي مُدرجة هنا لا مخفية: /api/contact (نموذج التواصل، الذي يخزّن ما تُرسله في Cloudflare D1 ويرسله إلينا بالبريد الإلكتروني). لا تُستدعى أثناء إنشاء رمز QR؛ فاحص أمان الروابط منتج منفصل على check.qr.abundera.ai. التحقق من أمان رمز QR
نموذج التهديد
بما أن المولّد لا يجمع ولا يخزن ولا ينقل أي بيانات مستخدم (نقطتا النهاية أعلاه تعالجان فقط ما تُرسله إليهما صراحةً)، فإن أكثر تهديدات تطبيقات الويب شيوعًا, سرقة بيانات الاعتماد، واختراق قواعد البيانات، واختطاف الجلسات، والحقن من جانب الخادم, لا تنطبق عليه. المساحة المتبقية للهجوم هي حزمة الأصول الثابتة (HTML وCSS وJavaScript) المقدَّمة من مصدرنا. نصمم بافتراض:
- اختراق المصدر: يستبدل المهاجم أحد أصولنا المجمَّعة بأصل ضار. يحدّ CSP أدناه من نطاق الضرر؛ وتجعل رموز cache-buster التراجع إلى نسخة معروفة الصحة أمرًا سهلًا.
- المدخلات المقدَّمة من المستخدم: حمولات QR، وأسماء vCard، وكلمات مرور Wi-Fi، وصفوف CSV دُفعية، ومحتويات الصور الممسوحة. كل مسار إدخال يُعامَل كمحتوى غير موثوق عند عرضه مجددًا في DOM.
- الصور المقدَّمة من المستخدم: روابط صور vCard وتحميلات الشعارات. تُعرض الصور في canvas ولا تُضمَّن أبدًا في DOM كترميز خام.
- إضافات المتصفح التابعة لجهات خارجية: خارج النطاق. إن كانت إضافة في متصفحك تملك صلاحية تعديل كل صفحة، فبإمكانها تعديل صفحاتنا. ضماناتنا سارية عند تحميل الصفحة دون تعديل.
سياسة أمان المحتوى, بحسب كل توجيه
السياسة أدناه تُقرأ من ملف _headers المنشور عند بناء هذه الصفحة، لذا لا يمكن أن تنحرف عمّا يرسله الطرف الطرفي. تحقّق منها في رؤوس الاستجابة لأي طلب.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com;
worker-src 'self' blob:;
style-src 'self' 'unsafe-inline' https://abundera.ai;
font-src 'self';
img-src 'self' data: blob: https:;
connect-src 'self' https: https://challenges.cloudflare.com;
frame-src 'self' https://challenges.cloudflare.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'ما يتيحه كل توجيه وأين يُقدِّم تنازلًا:
default-src 'self', الحد الأدنى الصارم. كل ما لا نُخفِّفه صراحةً يبقى محصورًا في نفس المصدر.script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com, لا<script>مضمَّن، ولاeval().abundera.aiهو نطاقنا الأصلي ويقدّم سكريبتًا مشتركًا واحدًا، مُبدّل اللغة.challenges.cloudflare.comهو أداة Turnstile المستخدمة في /contact/ فقط. يستلزم'wasm-unsafe-eval'مسار تجميع WebAssembly الخاص بمكتبة تشفير QR لدينا؛ وهو لا يُتيحeval()التقليدي. يفحص فحص ما قبل النشر لدينا كل صفحة HTML بحثًا عن سكريبتات مضمَّنة ويُخفق البناء عند اكتشافها.style-src 'self' 'unsafe-inline', تنازل حقيقي. يحسب معاينة QR لدينا الألوان على مستوى البكسل بشكل مضمَّن (سمات style على الوحدات الفردية). ستنجح قائمة السماح المعتمدة على التجزئة لكنها ستفشل عند أي تحديث للأنماط دون نشر. المقايضة: نقبل سياسة أنماط أضعف قليلًا؛ لا يستطيع التصميم تسريب البيانات على أي حال (لا يملك CSS نطاق وصولconnect-src).img-src 'self' data: blob: https:,data:لعروض QR المضمَّنة، وblob:لروابط تنزيل التصدير، وhttps:لروابط صور vCard والصور الرمزية المقدَّمة من المستخدم. الروابط المقدَّمة من المستخدم لا تُنفَّذ أبدًا، بل تُعرض فقط. لاحظ أن هذا التوجيه أيضًا قناة خارجة: أي سكريبت يعمل على الصفحة يمكنه طلب صورة من أي مضيف HTTPS ووضع بيانات في العنوان. نقبل ذلك لأن الميزة تحتاجه، ونقوله هنا بدلًا من وصف السياسة بأنها محكمة تمامًا.connect-src 'self' https: https://challenges.cloudflare.com, هذا هو التوجيه الذي يستحق قراءة متأنية.https:مصدر مخطط: يتيح لـfetch()وXMLHttpRequest وWebSocket عبر TLS وEventSource وsendBeacon()الوصول إلى أي مصدر HTTPS، لا فقط ما كتبته أنت. وُجد كي يتمكن حقل صورة vCard وفحص أمان الروابط من جلب عنوان لصقته. هذا لا يمنع التسريب عبر سكريبت خبيث؛ لا يفعل ذلك أي توجيه CSP بمجرد أن يعمل السكريبت بصفتنا نحن. ما يمنع التسريب هو أن كودنا المنشور لا يُصدر مثل هذه الطلبات، وهو ما يمكنك مراقبته في تبويب الشبكة.frame-src 'self' https://challenges.cloudflare.com, يمكن تضمين أداة Turnstile فقط في إطار.frame-ancestors 'none', لا يمكن لأي موقع آخر تضمينا. يمنع clickjacking.base-uri 'self', لا يستطيع وسم<base>مُحقَن بشكل ضار إعادة توجيه الروابط النسبية إلى مصدر المهاجم.form-action 'self', أي نموذج مُحقَن لا يمكنه الإرسال إلا إلى مصدرنا. نموذج التواصل هو النموذج الوحيد الذي يُرسل إلى أي مكان.
ما لا تستطيع CSP فعله
تحدّ CSP من الأماكن التي يُحمِّل منها المتصفح الكود. إنها لا تفحص ما يفعله الكود المُحمَّل. لو تعرّض مصدرنا للاختراق، وهو أول بند في نموذج التهديد، فسيعمل سكريبت المهاجم بنفس صلاحياتنا، بما في ذلك تخفيفات connect-src وimg-src أعلاه. دفاعاتنا ضد ذلك تقع فوق CSP: تخرج عمليات النشر عبر حساب Cloudflare واحد بتوكنات محدودة النطاق، وأسماء ملفات الأصول مُجزّأة بالمحتوى بحيث يغيّر أي ملف مُستبدَل كل عنوان يشير إليه، ويخزّن عامل الخدمة مؤقتًا استجابات GET من نفس المصدر فقط. يصف البيان الموقع كما شُحن. إنه ليس ادعاءً بأن لا صفحة ويب يمكنها إرسال بيانات إلى أي مكان أبدًا، ونفضّل أن تحاسبنا على العبارة الأضيق القابلة للتحقق.
تسري سياسات CSP مختلفة على /bio/* (تخفيف img-src للصور الرمزية المقدَّمة من المستخدم) و/embed/* (تخفيف frame-ancestors للتضمين المقصود). كلاهما موثَّق في site/_headers.
رؤوس النقل والإطارات
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload, سنة كاملة، جميع النطاقات الفرعية، مؤهَّل لقائمة HSTS preload. ترفض المتصفحات المتوافقة الرجوع إلى HTTP. - X-Frame-Options: DENY, يتكرر مع
frame-ancestorsفي CSP، لكنه محتفظ به لتغطية المتصفحات القديمة. - X-Content-Type-Options: nosniff, يمنع هجمات ارتباك MIME.
- Referrer-Policy: strict-origin-when-cross-origin, نقرات الروابط الخارجية تُسرِّب المصدر دون المسار.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=(), الكاميرا مسموح بها فقط لماسحنا الخاص؛ الميكروفون والموقع مرفوضان صراحةً حتى عند التضمين.
Service Worker
يخزن Service Worker لدينا (site/sw.js) مؤقتًا الأصول من المصدر ذاته فقط. يرفض معالج الجلب صراحةً الطلبات عبر المصادر والأساليب غير GET, يمكنك قراءة المنطق في /sw.js (المصدر يُشحن غير مُصغَّر بما يكفي لمتابعته). تُغلَّف عمليات الكتابة في الذاكرة المؤقتة بـevent.waitUntil() لئلا تُحذف في منتصف التنقل.
تعقيم المدخلات
كل مسار عرض يقبل مدخلات المستخدم يعاملها كنص غير موثوق:
- معاينات حمولة QR تستخدم
textContentلاinnerHTML. - أهداف المشاركة (الحافظة،
navigator.share()) تمرر نص المستخدم كسلسلة نصية لا كترميز. - تصدير SVG يُوَلَّد من مكتبة التشفير لدينا؛ محتوى المستخدم يُشفَّر بـbase64 في
xlink:hrefالخاص بـ<image>، ولا يُحقَن كعناصر SVG. - معاينات الطباعة تستخدم رابط blob لا
document.write(). - تحليل localStorage مُغلَّف بـ
try/catch, تُنتج إدخالات تالفة قيمة افتراضية فارغة جديدة، لا استثناءً قد يتكشَّف في مسار كود حيّ. - الروابط المقدَّمة من المستخدم والمعروضة كأزرار "فتح رابط" مقيَّدة بـ
http(s)://, تُرفض مخططاتjavascript:وdata:.
جلب الصور عبر المصادر
عندما يلصق المستخدم رابط https: كصورة vCard أو شعار، يجلبه المتصفح وفق CORS وقائمة سماح img-src في CSP. تُعرض الصورة في canvas. لا تصبح DOM حيًّا، ولا تُنفَّذ كود، ولا تصل إلى مصدرنا, الجلب يتم من المتصفح إلى الصورة البعيدة مباشرةً، والنتيجة تُرسم من جانب العميل. مهاجم يتحكم في رابط صورة بعيدة يمكنه تتبع تحميل الرابط (سطر سجل في خادمه) لكن لا يستطيع تسريب أي شيء من صفحتنا.
تكامل الموارد الفرعية (SRI)
جميع JavaScript وCSS التي نشحنها من المصدر ذاته. لا نحمِّل سكريبتات أو أوراق أنماط تابعة لجهات خارجية، لذا لا تنطبق تجزئات SRI. إن حمَّلنا أصلًا تابعًا لجهة خارجية مستقبلًا، سنضيف سمة integrity عليه ونوثِّق عملية تحديث التجزئة في هذه الصفحة.
الإبلاغ عن ثغرة أمنية
إن اكتشفت مشكلة أمنية تؤثر على Abundera QR, سواء في كودنا أو في عملية النشر أو في إحدى التبعيات التي نشحنها, يرجى الإبلاغ عنها بشكل خاص إلى security@abundera.ai. نهدف إلى الفرز خلال 72 ساعة. يمكنك أيضًا التواصل معنا عبر تفاصيل الاتصال في ملف /.well-known/security.txt.
لا برنامج مكافآت (حتى الآن)
لا نقدم مكافآت مدفوعة حاليًا، لكن كل تقرير صحيح مؤكَّد يحصل على ذكر في سجل التغييرات وشكرنا العلني.
التحقق من كل ما سبق
كل ادعاء في هذه الصفحة قابل للتحقق من DevTools المتصفح دون الحاجة إلى الوثوق بنا:
- CSP: DevTools ← Network ←
/← Headers. اقرأ رأس الاستجابةContent-Security-Policy. - HSTS ورؤوس الأمان: في المكان ذاته, كل
Strict-Transport-SecurityوX-Frame-Optionsوغيرها مرئية. - لا اتصالات خارجية: DevTools ← Network ← Fetch/XHR. ولِّد رمز QR. راقب العداد يبقى على صفر.
- نطاق Service Worker: DevTools ← Application ← Service Workers. تحقق من مصدر السكريبت وقائمة الأصول المخزَّنة مؤقتًا.
- لا كوكيز: DevTools ← Application ← Cookies. فارغ.
- جولة كاملة: قسم التحقق في البيان.
التواصل
الإفصاحات الأمنية: security@abundera.ai