یک محقق امنیتی به نام Dhruv برای ما ایمیل زد و یک مسیر XSS ذخیرهشده گزارش داد: کسی یک فایل PDF با جاوااسکریپت داخلش را به یک تیکت پشتیبانی FanDesk پیوست میکند، و وقتی کارشناس پشتیبانی آن را باز میکند، اسکریپت «در بستر همان اپلیکیشن» اجرا میشود.
وقتی بررسی کردیم چه دیدیم
مسیر دانلود در FanDesk از دسامبر ۲۰۲۵ همیشه Content-Disposition: attachment و application/octet-stream را اجباری کرده، این را با git blame چک کردیم. نمایشگر پیوست در فرانتاند هیچجا iframe، embed یا object ندارد؛ هر پیوست غیرتصویری از مسیر دانلود Blob رد میشود، نه رندر درونخطی. آپلودها هم از قبل با python-magic اعتبارسنجی میشوند، یعنی بایتهای واقعی فایل خوانده میشود نه نوعی که مرورگر ادعا میکند، و image/svg+xml دقیقاً به همین دلیل از فهرست مجاز کنار گذاشته شده، یک فایل SVG میتواند تگ <script> داشته باشد.
پس مسیری که Dhruv توصیف کرده بود احتمالاً همانطور که نوشته شده تکرار نمیشود. اما این دلیلی نیست که کد را همانطور رها کنیم.
با این حال چه چیزی را تغییر دادیم
اعتبارسنجی زمان آپلود فقط میگوید فایل وقتی آپلود شد چه بود. هیچچیز جلوی این را نمیگیرد که بایتهای ذخیرهشده بعداً از مسیر دیگری طور دیگری سرو شوند. این شکاف را با شناسایی دوبارهٔ نوع واقعی فایل در زمان دانلود هم بستیم، از همان بایتهای روی دیسک، نه نوعی که در دیتابیس ذخیره شده:
detected_type = magic.from_file(str(file_path), mime=True)
is_image = FileStorageService.renders_inline_safely(detected_type)فقط یک نوع تصویر مجاز بهصورت inline سرو میشود؛ هر PDF و هر چیزی که صراحتاً نمیشناسیم، مجبور به دانلود میشود، حالا با هدر X-Content-Type-Options: nosniff و Content-Security-Policy: default-src 'none'; sandbox روی پاسخ، تا حتی اگر مسیر کد دیگری در آینده فایل را مستقیم باز کند، مرورگر نتواند تصمیم دیگری بگیرد. چهار تست جدید برای همین منطق اضافه کردیم و همان روز روی پروداکشن رفت.
درس واقعی
گزارش باگی که دقیقاً همانطور که نوشته شده تکرار نمیشود، باز هم ارزش خواندن دقیق دارد. ایمیل Dhruv به یک شکاف واقعی اشاره کرد، نه یک شکاف قابلسوءاستفاده امروز، اما شکافی بین چیزی که در زمان آپلود اعتبارسنجی میکنیم و چیزی که در زمان دانلود بهش اعتماد میکنیم. بستن این شکاف یک بعدازظهر وقت گرفت. فهمیدن اینکه بهش نیاز داریم، توجه دقیق یک نفر دیگر را گرفت، و یک پاسخ تشکر از او را ارزشش داشت.
PDF حالا حامل اصلی فیشینگ با کد QR هم شده است؛ ببینید گزارش دفاع دیجیتال ۲۰۲۶ مایکروسافت درباره QR داخل PDF چه یافته.
نقصهای میز خدمت خطری نظری نیستند. در شهریور ۱۴۰۵ دو نقص روز صفر Zammad مهاجم را از میز خدمت عمومی DIVD به root رساند؛ ماجرا و چکلیستی برای سامانه تیکتینگ روی سرور خودتان را نوشتهایم.
یک نمونهٔ دیگر از امنیت درخواستهای پشتیبانی: نصبکنندهٔ جعلی Zoom و Adobe و ابزار مدیریت از راه دور.
پرسشهای متداول
آیا پیوست PDF میتواند در هلپدسک باعث XSS ذخیرهشده شود؟
اگر برنامه PDF را درون origin خودش بهصورت درونخطی نمایش دهد، بله، چون PDF میتواند جاوااسکریپت داخلی داشته باشد. الگوی امن این است که هر PDF و هر نوع فایل ناشناخته بهصورت پیوست دانلود شود، هدر X-Content-Type-Options: nosniff و Content-Security-Policy با default-src 'none'; sandbox فرستاده شود و فقط انواع تصویر مجاز درونخطی نمایش داده شوند.
چرا نوع MIME فایل را هنگام دانلود هم بررسی کنیم، نه فقط هنگام آپلود؟
اعتبارسنجی هنگام آپلود فقط میگوید فایل هنگام رسیدن چه بوده است. اگر بعدها مسیر کد دیگری تغییر کند، ممکن است بایتهای ذخیرهشده طور دیگری سرو شوند. تشخیص دوبارهٔ نوع واقعی از روی بایتهای روی دیسک هنگام دانلود، بهجای اعتماد به نوع ذخیرهشده در پایگاه داده، فاصلهٔ بین چیزی که اعتبارسنجی میکنید و چیزی که سرو میکنید را میبندد.
آیا آپلود SVG بهعنوان تصویر مجاز باشد؟
با احتیاط. فایل SVG میتواند تگ script داشته باشد، پس نمایش درونخطی آن میتواند کد را در origin برنامهٔ شما اجرا کند. فندسک image/svg+xml را صریحاً از فهرست مجاز نمایش درونخطی کنار گذاشته و آپلودها را با python-magic اعتبارسنجی میکند که بایتهای واقعی فایل را میخواند، نه نوعی که مرورگر ادعا میکند.


