💡 مثال عملي كامل متاح على GitHub:
skip-external-resources-when-signing-dotnet

المقدمة

يمكن لمستند Word أن يحتوي على صورة ليست داخل الملف. يحتفظ المستند بعنوان، وأي برنامج يفتحه يجلب ذلك العنوان. على سطح المكتب هذه ميزة – تُحدَّث الصورة عندما يتغير المصدر. على الخادم الذي يقبل التحميلات، يعني ذلك أن الشخص الذي أرسل لك الملف يقرر أي عناوين URL يطلبها بنية تحتية الخاصة بك.

تحميل المستند بأمان هو سلوك GroupDocs.Signature لـ .NET يرفض إجراء تلك الطلبات. اعتبارًا من الإصدار 26.9، يكون LoadOptions.SkipExternalResources مُعيَّنًا إلى true افتراضيًا. تقارن هذه المقالة بين أوضاع التحميل الثلاثة على نفس المستند، وتظهر كيفية السماح لمضيف واحد دون السماح للجميع، وتغطي لماذا لا تحتاج عملية التوقيع على ملف غير موثوق إلى أي وصول إلى الشبكة على الإطلاق.

لماذا هذا مهم أكثر مما يبدو

لهذا الهجوم اسم – طلبات الخادم الجانبية (SSRF) – وله ثلاث أشكال ملموسة.

عنوان داخلي لا يمكن الوصول إليه من الإنترنت يمكن الوصول إليه من خادمك، لذا يمكن لمستند مُصمم أن يجعل خدمتك تجلب http://169.254.169.254/ أو نقطة نهاية إدارية على localhost، وبحسب ما تفعله بالنتيجة، قد يتسرب ذلك. مسار UNC في مستند يمكن أن يدفع مضيف Windows إلى المصادقة الصادرة، مما يسلم بيانات الاعتماد إلى خادم يتحكم فيه المهاجم. ورابط إلى مضيف لا يجيب أبدًا يعلق خيط التحميل حتى ينتهي مهله، وهو طريقة رخيصة لاستنزاف مجموعة العاملين.

كنت أظن أن هذا مجرد قلق نظري حتى شاهدت مستند اختبار يجلب صورة عبر خدمة لا ينبغي لها أن تُجري طلبات صادرة على الإطلاق. لا يتطلب أي من هذا وجود خطأ في مكتبة المستندات. اتباع الرابط هو ما يطلبه التنسيق؛ السؤال هو فقط ما إذا كان يجب على خادمك الاستجابة لذلك.

الطريقة 1 – الإعداد الافتراضي الجديد

بدون أي LoadOptions:

using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);

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

ما هي الميزات التي تُعد خارجية؟ الصور المرتبطة بدلاً من المدمجة، حقول INCLUDEPICTURE، الصور المرتبطة في العروض التقديمية وجداول البيانات، والصور وأوراق الأنماط التي يشير إليها ملف SVG. المحتوى المدمج يبقى دون تغيير – فهو موجود بالفعل في الملف.

الطريقة 2 – السماح بعنوان واحد فقط (القائمة البيضاء)

العديد من المستندات ترتبط بمكان شرعي: CDN الشركة، خادم صور داخلي، متجر قوالب. اسمح بذلك ولا شيء غيره:

var loadOptions = new LoadOptions
{
    WhitelistedResources = new List<string> { trustedAddress }
};

using var signature = new Signature(sourcePath, loadOptions);

قواعد المطابقة تستحق الانتباه. إنها اختبار جزئي غير حساس لحالة الأحرف ضد عنوان المورد، مما يعني أن جزءًا قصيرًا قد يكون خطرًا: github يطابق github.attacker.example/payload.png بنفس السهولة التي يطابق بها المضيف الذي تقصده. استخدم مخططًا، مضيفًا ومسارًا – القائمة البيضاء في العينة تسمح بـ raw.githubusercontent.com/groupdocs-signature/.

الطريقة 3 – السماح بكل شيء

السلوك قبل الإصدار 26.9، لا يزال متاحًا:

var loadOptions = new LoadOptions { SkipExternalResources = false };

معقول للمستندات التي ينتجها تطبيقك نفسه. فخ واحد يستحق الإشارة إليه: الخاصية القديمة LoadExternalResources لها قطبية معاكسة، لذا SkipExternalResources = false هو ما يحل محل LoadExternalResources = true. إذا نسخت قيمة من الخاصية القديمة، فإنك تعكس وضع أمانك دون أي خطأ يُخبرك بذلك.

مقارنة الثلاثة: متى تستخدم كل منها

الوضع الأنسب لـ المزايا الرئيسية القيود
الافتراضي (تخطي) تحميلات المستخدمين، البريد الإلكتروني، ملفات الشركاء لا يمكن إجراء أي طلب صادر تُظهر الصور المرتبطة كعناصر نائبة
القائمة البيضاء المستندات التي ترتبط بمضيف تملكه تحافظ على عمل الروابط الشرعية مطابقة الجزئية تحتاج إلى جزء طويل ومحدد
السماح للجميع الملفات التي تولدها أنظمتك تظهر المعاينات كما كانت تمامًا يعيد تعرض SSRF الذي أُزيله في الافتراضي

ماذا عن التوقيع – هل يحتاج إلى الموارد؟

لا، وهذا هو الفائدة العملية. يتم تطبيق توقيع رمز QR باستخدام إعدادات التحميل الافتراضية ولا يُطلب أي مورد خارجي أثناء تحميل المستند أو توقيعه أو حفظه:

var options = new QrCodeSignOptions("Approved by GroupDocs.Signature")
{
    EncodeType = QrCodeTypes.QR,
    Left = 400,
    Top = 50,
    Width = 120,
    Height = 120
};

SignResult result = signature.Sign(outputPath, options);

يحافظ الناتج الموقّع على رابطه، لذا فإن المستخدم الذي يفتح المستند لاحقًا لا يزال يرى الصورة تُحل على جهازه الخاص. التخطي هو سياسة على مستوى الخادم، وليس تعديلًا للمستند – وهذا ما يجعله آمنًا للتطبيق على الملفات التي تتعامل معها نيابة عن شخص آخر.

ما الذي يتغير عند الترقية

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

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

أداة المعاينة، لأنها ليست واضحة

اثنان من الأوضاع الثلاثة أعلاه يستدعيان أداة مساعدة صغيرة، ومن المفيد إظهارها لأن PreviewOptions لا تقبل مسارًا:

var previewOptions = new PreviewOptions(
    pageData => File.Create(previewPath),
    (pageData, pageStream) => pageStream.Dispose())
{
    PreviewFormat = PreviewOptions.PreviewFormats.PNG
};

signature.GeneratePreview(previewOptions);

تأخذ مُنشئين للتيار – أحدهما لإنشاء تيار لكل صفحة، والآخر لإطلاقه. المستند العيني يحتوي على صفحة واحدة، لذا يُكتب ملف واحد؛ بالنسبة للمدخلات متعددة الصفحات، ضع رقم الصفحة في اسم الملف وإلا سيكتب كل صفحة فوق الأخيرة.

أفضل الممارسات

  • اعتبر أي شيء لم تقم بإنشائه غير موثوق، بما في ذلك الملفات من شركاء يتمتعون بوضع أمان جيد.
  • اجعل أجزاء القائمة البيضاء طويلة بما يكفي لتكون غير غامضة، وراجعها عندما يتغير CDN.
  • لا تقم أبدًا بتعيين SkipExternalResources من قيمة كانت تُعطى مسبقًا إلى LoadExternalResources.
  • تحقق من أحجام الناتج بدلاً من الاعتماد على الإعداد؛ التكوين الذي يبدو صحيحًا والطلب الذي لم يحدث هما ادعاءان مختلفان.

ما يخص SVG

من الجدير الإشارة إليه بشكل منفصل، لأن SVG هو كل من صيغة تحميل شائعة ومتجه SSRF شائع. يمكن لملف SVG أن يشير إلى صور وأوراق أنماط عبر URL، وتلك الإشارات تُعد موارد خارجية وفقًا لنفس القاعدة – تُتخطى افتراضيًا، ويمكن وضعها في القائمة البيضاء، ويمكن استعادتها. الخدمة التي تقبل صور SVG أو شعارات وتُظهرها على الخادم هي بالضبط الشكل الذي يحميه هذا التغيير.

إذا كان خط أنابيبك يقبل SVG من المستخدمين، فإن الإعداد الافتراضي هو ما تريد، والقائمة البيضاء مخصصة للحالة التي تسحب فيها القوالب الخاصة بك ورقة أنماط مشتركة من مضيف تديره.

الخلاصة

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

موارد إضافية