💡 مثال كامل يعمل متاح على GitHub: merge-untrusted-html-uploads-dotnet

دمج مستندات HTML غير موثوقة بأمان هو سير عمل GroupDocs.Merger لـ .NET يرفض الموارد الخارجية التي يشير إليها التحميل - عناوين URL البعيدة، مسارات UNC والملفات المحلية - ما لم تسمح بها صراحةً.

الطريقة القديمة كانت مؤلمة

تخيل أن بوابة الفوترة الخاصة بك تقوم بدمج تحميل فاتورة العميل مع ملاحظة تسليم من نفس الطلب في مستند HTML واحد للأرشفة. الفاتورة هي HTML عادي - رابط ورقة أنماط، صورة شعار، بضع فقرات - إلا أنك لم تكتبها، بل قام المتصفح أو أداة التصدير الخاصة بالعميل بذلك. قبل إصدار GroupDocs.Merger 26.10، كان الدمج يحمل الموارد الخارجية التي يشير إليها HTML - أوراق الأنماط البعيدة، الصور البعيدة، مسارات الملفات المحلية المطلقة - ولم يكن هناك خيار لإيقاف ذلك.

هذه مشكلة في اللحظة التي لا يكون فيها التحميل موثوقًا بالكامل. يمكن أن يكون عنوان URL لورقة الأنماط أيضًا بكسل تتبع. يمكن أن يشير src لصورة إلى http://169.254.169.254/latest/meta-data/iam/security-credentials/ - عنوان بيانات تعريف سحابي - وستقوم خطوة الدمج بطلبه نيابة عن الرافع. كما تم قراءة مسار file:/// مطلق، لذا يمكن لإشارة إلى تحميل عميل آخر سحب ذلك الملف إلى الناتج المدمج.

التكلفة الحقيقية للدمج العشوائي: مهمة دمج تجلب كل ما يطلبه التحميل تحول خادمك إلى وكيل لهجمات SSRF، وإشارة file:/// إلى وسيلة لقراءة دمج عميل واحد لملف عميل آخر.

هناك طريقة أفضل

يقوم GroupDocs.Merger لـ .NET بحل هذه المشكلة بشكل افتراضي. منذ الإصدار 26.10، كل مُنشئ LoadOptions يضبط SkipExternalResources = true، وMerger الذي يُنشأ بدون خيارات تحميل يستخدم هذا الإعداد الافتراضي نفسه - لا يتم تحميل أي شيء خارجي ما لم تقم بالسماح بذلك صراحةً.

المتطلبات المسبقة:

  • .NET (أي من الأطر الأربعة المستهدفة التي يدعمها GroupDocs.Merger لـ .NET)
  • GroupDocs.Merger لـ .NET الإصدار 26.10 أو أحدث (احصل على ترخيص مؤقت)
  • مستندان HTML للدمج، على الأقل أحدهما من مصدر لا تثق به بالكامل

ثبت الحزمة بأمر واحد:

dotnet add package GroupDocs.Merger --version 26.10.0

الطريقة الجديدة: دمج HTML دون الوثوق به

إليك ما يبدو عليه دمج تحميلين HTML مع الإعدادات الآمنة الافتراضية، ومعالج يسجل كل قرار، - وفي مرحلة ثانية - معالج يزود بأصل موثوق واحد.

الخطوة 1 - إنشاء Load Options للتنسيق

LoadOptions لا يحتوي على مُنشئ بدون معلمات وهو مغلق منذ الإصدار 26.10؛ يحدد المثال التنسيق صراحةً.

// LoadOptions(FileType.HTML) already denies everything external by default
var audit = new ResourceAudit();
var loadOptions = new LoadOptions(FileType.HTML) { ExternalResourceHandler = audit };

الخطوة 2 - دمج التحميلات وفحص القرارات

// Both uploads follow the options of the Merger they are joined to
using (var merger = new Merger(firstUpload, loadOptions))
{
    merger.Join(secondUpload);
    merger.Save(outputPath);
}

foreach (string decision in audit.Decisions)
{
    Console.WriteLine(decision);
}

النقاط الرئيسية:

  • SkipExternalResources: ضمنيًا true هنا، لذا يتم رفض كل عنوان URL بعيد، مسار UNC، وإشارة ملف محلي، بما في ذلك ملف في مجلد التحميل نفسه.
  • LoadOptions واحد لكل دمج: التحميل الثاني لا يحصل على خياراته الخاصة - إنه يرث ما تم إنشاء Merger الذي تم دمجه به به.

الخطوة 3 - السماح بوجود مصدر موثوق واحد فقط

// Supply the logo's bytes directly instead of requesting it over the network
var trustedAssets = new Dictionary<string, byte[]>
{
    [logoUrl] = File.ReadAllBytes(logoPath)
};
var audit = new ResourceAudit(trustedAssets);
var loadOptions = new LoadOptions(FileType.HTML) { ExternalResourceHandler = audit };

طريقة OnLoading في المعالج تضبط args.Skipped = false وargs.Data إلى تلك البايتات عندما يتطابق URI المطلوب، وبالتالي يحصل المستند على الشعار دون أن يقوم GroupDocs.Merger بإجراء أي طلب.

عند تشغيل الدمج الافتراضي ضد تحميلات الفاتورة وملاحظة التسليم، تم حظر جميع المراجع الخارجية السبعة عبر الملفين - ورقة الأنماط البعيدة، الشعار البعيد، عنوان URL بنمط البيانات التعريفية، صورة المجلد الشقيق، مسار file:///، مسار UNC، وعنوان تتبع مع سلسلة استعلام. يبقى joined.html يحتوي على النص من كلا التحميلين؛ يُستبدل كل صورة محجوبة بعنصر نائب. مع إرفاق معالج الشعار الموثوق، تم إرجاع الشعار كـ supplied والبقية الستة بقيت blocked.

مقارنة جنبًا إلى جنب: قبل vs. بعد

قبل (بدون خيار) بعد (GroupDocs.Merger 26.10)
الطلبات الخارجية كل إشارة تم تحميلها تلقائيًا مرفوض ما لم يُسمح به صراحةً
مسارات الملفات المحلية المطلقة تم تحميلها إلى الناتج مرفوضة مثل أي إشارة أخرى
استثناءات موثوقة غير ضرورية - كل شيء تم تحميله مورد واحد عبر معالج، أو بادئة في القائمة البيضاء
إدخال غير صالح في القائمة البيضاء لم توجد قائمة بيضاء مرفوض بـ ArgumentException عند الإنشاء
الكود المطلوب لا توجد طريقة لتقييد التحميل بضع أسطر على LoadOptions

التباين يكمن في ما هو ممكن، وليس فقط ما هو مريح: قبل 26.10 لم يكن هناك إعداد يوقف التحميل؛ بعده، الرفض هو الإعداد الافتراضي وكل استثناء صريح.

هل سيؤدي هذا إلى كسر HTML الذي يعتمد بالفعل على تحميل الصور البعيدة؟

إذا كان ذلك الـ HTML لا يزال يُدمج باستخدام LoadOptions الافتراضية، فالجواب نعم - لن تُحمَّل الصور، وسيُستبدل كل منها بعنصر نائب في الناتج. الإصلاح صريح، ليس حلاً مؤقتًا: قم بإدراج المصادر الموثوقة في WhitelistedResources، أو أرفق IExternalResourceHandler يزود بالأصول المحددة التي لا تزال تريد عرضها. في كلتا الحالتين، يستمر الدمج في العمل؛ لكنه يتوقف عن جلب أي شيء لم تُحدده.

مثال واقعي: دمج الفاتورة وملاحظة التسليم

خط أنابيب المستندات الذي يدمج HTML المقدم من العميل إلى نسخة أرشيفية هو بالضبط السيناريو الذي يُنمذج مشروع العينة في هذه الصفحة: تحميل فاتورة وملاحظة تسليم من نفس الطلب، مدموجين في ملف واحد. لا أحد من التحميلين هو ما كتبه فريق الخط نفسه، لذا لا يمكن افتراض أنهما آمانان للقراءة حرفيًا. الفاتورة تُظهر مبلغًا مستحقًا وعميلًا، Northwind Traders، وتحمل إشارة إلى شعار، ورقة أنماط تتبع، عنوان URL بنمط بيانات تعريف سحابي، إشارة إلى صورة في نفس المجلد، ومسار file:/// - سبعة مراجع خارجية بين التحميلين، ولا ينبغي لخط الأرشفة جلب أي منها نيابة عن العميل.

قبل تشغيل مهمة الدمج على الهواء، من المفيد تحديد، نوع المستند تلو الآخر، ما إذا كان هناك حاجة لأي استثناء على الإطلاق. سيناريو الفوترة في هذه العينة احتاج إلى استثناء واحد فقط: شعار الشركة نفسه، المقدم من ملف موجود على القرص بدلاً من جلبه من عنوان URL البعيد الذي يذكره HTML. كل ما تبقى - ورقة الأنماط المتعقبة، عنوان URL بنمط البيانات التعريفية، الملف الشقيق، مسارات UNC وfile:/// - لم يكن له سبب مشروع للتحميل، لذا كانت الإعدادات الافتراضية هي الجواب الصحيح لها.

// Reject a whitelist entry that is not an absolute URI prefix
var loadOptions = new LoadOptions(FileType.HTML)
{
    WhitelistedResources = new List<string> { "cdn.example.com" }
};

تمرير اسم مضيف مجرد بدلاً من بادئة مطلقة مثل https://cdn.example.com/brand/ يؤدي إلى رمي استثناء، مع الرسالة Whitelisted resource 'cdn.example.com' must be an absolute URI without a query, fragment or encoded '/', '\' or '.'. (Parameter 'WhitelistedResources') - قبل حتى فتح أي من التحميلين. التقاط قائمة بيضاء غير مُكوَّنة بشكل صحيح عند بدء التشغيل يساوي قيمة أكبر من التقاطها بعد فشل مورد بصمت في التحميل في بيئة الإنتاج.

ماذا يمكنك أن تفعل أيضًا مع GroupDocs.Merger لـ .NET؟

  • التحقق من صحة إدخالات القائمة البيضاء مبكرًا: إدخال WhitelistedResources غير صحيح يفشل عند إنشاء Merger، لذا تلتقط فحوصات بدء تشغيل النشر ذلك قبل معالجة أي مستند.
  • تزويد الأصول الموثوقة من القرص: يمكن لـ IExternalResourceHandler الرد على طلب ببايتات لديك بالفعل، دون أي وصول إلى الشبكة.
  • تطبيق سياسة واحدة على كل مستند مدموج: يتم ضبط LoadOptions مرة واحدة على Merger، ويتبع كل مستند يُدمج لاحقًا القواعد نفسها.
  • تغطية أكثر من HTML: معالجة النصوص، HTML/MHTML، العروض التقديمية، جداول البيانات، SVG ومستندات EPUB جميعها تحترم نفس الخيارات.

الخلاصة

دمج HTML الذي لم تكتبه لم يعد يعني الوثوق بكل ما يحتويه. يقوم GroupDocs.Merger لـ .NET برفض الموارد الخارجية افتراضيًا منذ الإصدار 26.10، والطريقتان لمنح استثناء - معالج الأصول الموثوقة وWhitelistedResources - يمنحانك بالضبط ما تحتاجه الأنابيب من وصول، ولا أكثر.

هل أنت مستعد لتجربتها بنفسك؟

موارد إضافية