مقاله فنی

بازپخش ایمن EMF و WMF نامطمئن در HotXLS

یک workbook اکسل می‌تواند تصاویر EMF و WMF حمل کند، و راه متعارف کشیدن یکی سپردن جریان بایت به پخش‌کننده متافایل سیستم‌عامل است. آن تصمیمی است که ارزش نگاه مستقیم دارد: یک متافایل یک جریان فرمان سریال‌شده برای یک API گرافیکی است، و بازپخشش یعنی گذاشتن فایلی که از طریق ایمیل رسیده راننده درایور گرافیک باشد. HotXLS مسیر دیگر را برمی‌دارد. XLSDecodeVectorScene خود متافایل را تجزیه می‌کند، هدر، هر اندازه رکورد، مجموع رکورد اعلام‌شده و جای‌گذاری دقیق رکورد انتهای فایل را اعتبارسنجی می‌کند، رکوردهای escape را کلاً رد می‌کند، و یک TXLSVectorScene از فرمان‌های ترسیم اولیه برمی‌گرداند که بک‌اندهای Canvas و SVG از طریق کد خودشان بازپخش می‌کنند. هیچ playback درایوری در هیچ نقطه‌ای دخیل نیست

HotXLS بایت‌های نامطمئن متافایل کاربرگ EMF و WMF را با XLSDecodeVectorScene به فهرست فرمان TXLSVectorScene تجزیه می‌کند به‌جای playback متافایل GDI
HotXLS خود متافایل را تجزیه و فرمان‌های اولیه را برای بازپخش Canvas و SVG برمی‌گرداند؛ مسیر متعارف جریان بایت را روی پشته گرافیک اجرا می‌کند

معامله پوشش در برابر مهار است. یک whitelist فرمان مستطیل‌محور هر متافیلی را که یک طراح می‌تواند بسازد بازتولید نمی‌کند، پس صحنه گزارش می‌دهد چند رکورد ترسیم را نتوانست نمایش دهد و فراخواننده تصمیم می‌گیرد با آن چه کند. برای یک فرایند سرور که اسنادی را رندر می‌کند که خودش نساخته، آن معامله جهت درستی دارد

چرا playback متافایل برای ورودی نامطمئن تناسب بدی دارد؟

چون فرمت یک عکس نیست، یک برنامه است. یک جریان رکورد EMF پشته وضعیت device context را دستکاری می‌کند، آبجکت‌ها را از یک جدول هندل تخصیص و انتخاب می‌کند، و می‌تواند رکوردهای escape حمل کند که payloadشان به یک درایور دستگاه پاس می‌شود. بازپخش آن مسیرهایی در پشته گرافیک پلتفرم را ورز می‌دهد که با این فرض نوشته شده‌اند که متافایل از یک برنامه همکاری‌کننده روی همان دستگاه آمده. وقتی ورودی یک پیوست صفحه‌گسترده است، آن فرض رفته است، و هیچ میزان مراقبتی درون کتابخانه صفحه‌گسترده کمکی نمی‌کند چون کتابخانه مؤلفه‌ای نیست که تجزیه می‌کند

این همان استدلالی است که بر لایه ظرف حاکم است. یک workbook یک آرشیو ZIP است، و HotXLS دایرکتوری مرکزی‌اش را اعتبارسنجی می‌کند به‌جای اعتماد به آفست‌های اعلام‌شده، همان‌طور که در مقاله اعتبارسنجی ZIP end-of-central-directory توصیف شده است. payloadهای متافایل لایه بعدی همان مساله‌اند

رمزگشا پیش از کشیدن هر چیزی چه چیزهایی را بررسی می‌کند

اعتبارسنجی ساختاری است و از پیش رخ می‌دهد، چون تجزیه‌گری که کشیدن را شروع و همزمان اعتبارسنجی می‌کند از قبل روی داده‌ای عمل کرده که راستی‌آزمایی نکرده. هدر باید سخت‌گیرانه مطابقت کند نه ظاهراً. هر رکورد باید اندازه‌ای اعلام کند که درون بافر باقی‌مانده جا شود و به‌اندازه کافی بزرگ برای فیلدهای ثابت خودش باشد. تعداد رکوردی که هدر اعلام می‌کند باید با رکوردهای واقعاً حاضر مطابقت کند. رکورد انتهای فایل باید دقیقاً همان‌جایی بنشیند که جریان تمام می‌شود، نه صرفاً جای نزدیکی از آن، که ترفند زباله-انتهایی را که payload دومی را پشت یک عکس معتبر قایم می‌کند می‌بندد

فراتر از ساختار، رمزگشا روی معناشناسی fail-closed است. رکوردهای escape مردود می‌شوند، نه اینکه skip شوند. رکورد تغییردهنده وضعیتی که رمزگشا مدل نمی‌کند باعث شکست decode می‌شود به‌جای نادیده گرفته‌شدن، چون نادیده‌گرفتن یک تغییر وضعیت یعنی هر فرمان ترسیم بعدی در وضعیتی اجرا می‌شود که فایل درخواستش را نکرده، و نتیجه عکسی است که به شکلی غلط است که هیچ‌کس نمی‌تواند پیش‌بینی کند. رکوردهای ترسیم بیرون از مجموعه فرمان پشتیبانی‌شده مساله دیگری‌اند: آن‌ها شمرده و skip می‌شوند، چون یک شکل غایب یک شکاف مرئی و قابل‌گزارش است نه یک خرابی بی‌صدا

XLSDecodeVectorScene هدر، اندازه‌های رکورد، مجموع‌ها و جای‌گذاری EOF را از پیش بررسی می‌کند، سپس رکوردهای escape را رد و رکوردهای ترسیم پشتیبانی‌نشده را می‌شمارد
بررسی‌های ساختاری از پیش اجرا و معناشناسی fail-closed رکوردهای escape را مردود می‌کند، در حالی که رکوردهای ترسیم پشتیبانی‌نشده فقط شمرده و skip می‌شوند

بودجه‌ها بخشی از قرارداد فرمت‌اند

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

// از lxVectorScene: بودجه decode، بیان‌شده نه ضمنی
XL_VECTOR_MAX_RECORDS           = 1000000;
XL_VECTOR_MAX_HANDLES           = 4096;
XL_VECTOR_MAX_DC_DEPTH          = 32;
XL_VECTOR_MAX_COMMANDS          = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS      = 2000000;
XL_VECTOR_MAX_TEXT_CHARS        = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS  = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE        = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS      = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES       = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD             = 1000000000;

دو مورد از این‌ها ارزش یک یادداشت دارند. سقف عمق device context برابر ۳۲ وجود دارد چون رکوردهای SaveDC و RestoreDC تو در تو می‌شوند، و یک جریان نامتوازن می‌تواند تا ابد push کند؛ ۳۲ برای متافایل‌های واقعی بخشنده و برای اجرا ارزان است. سقف مختصات وجود دارد چون مختصات به یک transform تغذیه می‌شوند، و مقداری نزدیک حدود بازه عدد صحیح نتیجه تبدیل‌شده‌ای تولید می‌کند که یا بی‌نهایت است یا wrap می‌شود، که بعدش هر محاسبه bounding box در پایین‌دست بی‌معناست. کلمپ‌کردن مختصات در زمان تجزیه بسیار راحت‌تر استدلال‌پذیر است تا دفاع از هر مصرف‌کننده هندسه

ثابت‌های بودجه decode در lxVectorScene مربوط به HotXLS برای رکوردها، هندل‌ها، عمق DC، فرمان‌ها، نقاط، متن، اندازه تصویر و کلمپ مختصات
هر حد یک ثابت نام‌دار است که حین تجزیه اجرا می‌شود؛ سقف عمق DC و کلمپ مختصات بیشترین توجه را می‌طلبند

استفاده از صحنه

رمزگشا یک آبجکت که مالکش هستید، یک تعداد فرمان، یک اندازه اسمی، و تعداد رکوردهای ترسیمی را که نمایششان را انتخاب نکرد برمی‌گرداند

uses
  lxVectorScene;

var
  Scene: TXLSVectorScene;
  Error: WideString;
  I: Integer;
begin
  // متغیر Data بار خام عکس گرفته‌شده از workbook را نگه می‌دارد
  if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
  begin
    // مردود: هدر، کران‌ها، مجموع‌ها، جای‌گذاری EOF یا یک بودجه
    LogReject('metafile rejected: ' + Error);
    Exit;
  end;
  try
    if Scene.SkippedDrawRecords > 0 then
      LogWarning(Format('%d drawing records outside the safe subset',
        [Scene.SkippedDrawRecords]));
    for I := 0 to Scene.Count - 1 do
      case Scene.Commands[I].Kind of
        xlsvcRectangle: DrawRect(Scene.Commands[I]);
        xlsvcEllipse:   DrawEllipse(Scene.Commands[I]);
        xlsvcPolyline,
        xlsvcPolygon,
        xlsvcBezier:    DrawPath(Scene.Commands[I]);
        xlsvcText:      DrawText(Scene.Commands[I]);
        xlsvcImage:     DrawImage(Scene.Commands[I]);
      end;
  finally
    Scene.Free;
  end;
end;

رکورد فرمان هر چه یک بک‌اند لازم دارد را حمل می‌کند و هیچ چیزی که به دستگاه نیاز دارد نه: حضور قلم، رنگ، پهنا و سبک؛ حضور قلم‌مو و رنگش؛ هندسه؛ و برای متن، رشته، نام فونت، اندازه، سبک‌ها و تراز. همین است که همان صحنه را هم برای رندرکننده canvas روی صفحه و هم برای نویسنده SVG قابل استفاده می‌کند، و دلیل اینکه مسیر وکتوری میان پیش‌نمایش و صادرات واگرا نمی‌شود. رندر روی صفحه محتوای کاربرگ به‌طور کلی در مقاله رندر گرید VCL سفارشی پوشش داده شده است

مردود کردن یک عکس به workbook آسیب نمی‌زند

یک پراپرتی مهم این طراحی این است که decode مردودشده فقط رندر را متأثر می‌کند. payload اصلی در مدل می‌ماند، پس workbookی که باز و دوباره ذخیره می‌شود تصاویر متافایلش را بایت‌به‌بایت بیرون می‌برد، فارغ از اینکه رمزگشای امن می‌توانست آن‌ها را بکشد یا نه. مسیر raster کران‌دار موجود هم به‌عنوان fallback همچنان در دسترس است. به بیان دیگر، تجزیه‌گر سخت‌گیر آنچه را اجرا می‌شود کنترل می‌کند نه آنچه را حفظ می‌شود، که همان تمایزی است که اجازه می‌دهد یک تغییر با انگیزه امنیتی منتشر شود بدون آنکه به یک تغییر ازدست‌دادن داده تبدیل شود

هندل‌کردن آبجکت‌های ترسیمی به‌طور کلی، شامل بخش‌های مدل آبجکت که رفت‌وبرگشت‌ها را دست‌نخورده طی می‌کنند، در مقاله نمودارها، تصاویر و ترسیم‌ها پوشش داده شده است

این یک استقرار سرور را کجا می‌گذارد

اگر workbookهای آپلودشده توسط کاربر را در یک سرویس رندر می‌کنید، موضع عملی اکنون قابل دفاع است: تصاویر متافایل توسط کدی تجزیه می‌شوند که می‌توانید ممیزی‌اش کنید، با ثابت‌هایی کران‌دار می‌شوند که می‌توانید بخوانید، و هرگز به یک درایور گرافیک سپرده نمی‌شوند. هشدار صادقانه پوشش است. متافایل‌های پیچیده تولید ابزارهای ترسیم به شمارنده رکوردهای skip شده می‌خورند، و پاسخ آن آشکارکردن شمارنده است نه پهن‌کردن بی‌سروصدای whitelist. عکسی که نیمه‌کاره رندر می‌شود و می‌گوید یک گفت‌وگوی پشتیبانی است؛ عکسی که غلط رندر می‌شود و هیچ نمی‌گوید یک گزارش باگ از مشتری است

HotXLS فرمت‌های XLS، XLSX، ODS و CSV را به‌طور بومی در Delphi و C++Builder بدون نصب Excel هندل می‌کند، و همان فلسفه تجزیه کران‌دار در لایه‌های ظرف، فرمول و ترسیم آن جاری است. جزئیات فرمت و امنیت در صفحه محصول HotXLS Delphi spreadsheet component فهرست شده است