💡 مثال کامل کاری در 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 در سند می‌تواند یک میزبان ویندوزی را به احراز هویت خروجی وادار کند و اعتبارنامه‌ها را به سرور کنترل‌شده توسط مهاجم بدهد. و یک لینک به میزبانی که هرگز پاسخ نمی‌دهد، نخ بارگذاری را تا زمان انقضا مشغول می‌کند، که راهی ارزان برای خسته کردن یک استخر کارگر است.

من این را تا زمانی که یک سند آزمایشی تصویری را از طریق سرویسی که اصلاً نباید درخواست‌های خروجی داشته باشد، دریافت کرد، به‌عنوان یک نگرانی نظری در نظر می‌گرفتم. هیچ‌یک از این‌ها نیاز به باگ در کتابخانهٔ سند ندارند. دنبال کردن یک لینک همان کاری است که فرمت می‌خواهد؛ سؤال فقط این است که آیا سرور شما باید این کار را بکند یا نه.

روش ۱ – پیش‌فرض جدید

بدون هیچ LoadOptions:

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

هیچ چیزی دریافت نمی‌شود. پیش‌نمایش یک جای‌نگهدار خالی را در جایی که تصویر لینک‌شده قرار می‌گیرد، رندر می‌کند و فایل PNG کوچکتر از حالت عادی است. این تفاوت اندازه، واضح‌ترین مدرکی است که نشان می‌دهد هیچ درخواستی از ماشین خارج نشده است.

کدام ویژگی‌ها به‌عنوان خارجی محسوب می‌شوند؟ تصاویر لینک‌شده به‌جای تعبیه‌شده، فیلدهای INCLUDEPICTURE، تصاویر لینک‌شده در ارائه‌ها و صفحات گسترده، و تصاویر و شیوه‌نامه‌هایی که یک SVG به آن‌ها ارجاع می‌دهد. محتوای تعبیه‌شده دست‌نخورده می‌ماند – زیرا از پیش در فایل وجود دارد.

روش ۲ – سفید‑لیست کردن یک آدرس

سندهای زیادی به مکان‌های معتبر لینک می‌شوند: 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/ را شامل می‌شود.

روش ۳ – اجازه دادن به همه

رفتار پیش از نسخه 26.9، که هنوز در دسترس است:

var loadOptions = new LoadOptions { SkipExternalResources = false };

برای اسنادی که برنامهٔ خودتان تولید کرده است، منطقی است. یک تلهٔ مهم: ویژگی منسوخ LoadExternalResources قطبیت مخالف دارد، بنابراین SkipExternalResources = false همان چیزی است که LoadExternalResources = true را جایگزین می‌کند. اگر مقدار را از ویژگی قدیمی کپی کنید، بدون هیچ خطایی وضعیت امنیتی خود را برعکس می‌کنید.

مقایسهٔ سه حالت: چه زمانی از کدام استفاده کنیم

حالت مناسب برای مزایای کلیدی محدودیت‌ها
پیش‌فرض (skip) بارگذاری‌های کاربر، ایمیل، فایل‌های شریک هیچ درخواست خروجی امکان‌پذیر نیست تصاویر لینک‌شده به‌صورت جای‌نگهدار نمایش داده می‌شوند
سفید‑لیست اسنادی که به میزبانی که شما مالک آن هستید لینک می‌دهند لینک‌های معتبر کار می‌کنند تطبیق زیررشته‌ای نیاز به یک بخش طولانی و مشخص دارد
اجازه به همه فایل‌هایی که سیستم‌های شما تولید کرده‌اند پیش‌نمایش‌ها دقیقاً همان‌طور که قبلاً بودند نمایش داده می‌شوند خطر SSRF که پیش‌فرض حذف کرده بود، باز می‌گردد

آیا امضا نیاز به این منابع دارد؟

نه، و این همان سود عملی است. یک امضای QR‑code با تنظیمات پیش‌فرض بارگذاری اعمال می‌شود و در حین بارگذاری، امضا یا ذخیره‌سازی سند، هیچ منبع خارجی درخواست نمی‌شود:

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 را از کاربران می‌پذیرد، پیش‌فرض همان تنظیمی است که می‌خواهید، و سفید‑لیست برای مواردی است که قالب‌های خودتان یک شیوه‌نامهٔ مشترک را از میزبانی که شما اداره می‌کنید، می‌کشند.

نتیجه‌گیری

پیش‌فرض طوری تغییر کرد که رفتار پرخطر نیاز به تصمیم صریح دارد و رفتار ایمن نیازی به کاری ندارد. پیش‌فرض را برای ورودی‌های غیرقابل اعتماد نگه دارید، سفید‑لیست را به‌صورت محدود برای میزبانی که خودتان کنترل می‌کنید اعمال کنید، و به یاد داشته باشید که خود امضا هرگز به شبکه نیاز نداشت. اجرای نمونه بر روی یکی از اسناد خودتان تنها یک دقیقه طول می‌کشد و به شما، در سه اندازهٔ فایل، دقیقاً می‌گوید سرویس شما چه چیزی را دریافت کرده است.

منابع اضافی