مقال تقني

إعادة تشغيل EMF وWMF غير الموثوقة بأمان في HotXLS

يمكن لمصنف Excel أن يحمل صور EMF وWMF، والطريقة الاعتيادية لرسم واحدة تسليم مسار البايتات إلى مشغل الملفات التعريفية في نظام التشغيل. وذلك قرار يستحق النظر فيه مباشرة: الملف التعريفي مسار أوامر متسلسل لواجهة رسم، وإعادة تشغيله تعني ترك ملف وصل عبر البريد الإلكتروني يقود مشغل الرسوميات. يسلك HotXLS الطريق الآخر. يحلل XLSDecodeVectorScene الملف التعريفي بنفسه، ويتحقق من الترويسة وكل حجم سجل ومجموع السجلات المصرح به والموضع التام لسجل نهاية الملف، ويرفض سجلات escape قاطعة، ويعيد TXLSVectorScene من أوامر رسم أولية يعيد مسارا Canvas وSVG تشغيلها عبر كودهما الخاص. ولا تدخل عملية تشغيل بالمشغل في أي نقطة

يحلل HotXLS بايتات EMF وWMF غير الموثوقة لأوراق العمل بـ XLSDecodeVectorScene إلى قائمة أوامر TXLSVectorScene بدل تشغيل الملف التعريفي عبر GDI
يحلل HotXLS الملف التعريفي بنفسه ويعيد أوامر أولية لتشغيلها Canvas وSVG؛ والطريق الاعتيادي ينفذ مسار البايتات على كومة الرسوميات

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

لماذا يعد تشغيل الملف التعريفي ملائما سيئا للمدخلات غير الموثوقة؟

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

هذا هو الاستدلال نفسه الذي يحكم طبقة الحاوية. المصنف أرشيف ZIP، ويتحقق HotXLS من دليله المركزي بدل الوثوق بالإزاحات المصرح بها، كما هو موصوف في مقال التحقق من نهاية الدليل المركزي لـ ZIP. وحمولات الملفات التعريفية هي الطبقة التالية من المشكلة نفسها

ماذا يفحص المفكك قبل أن يرسم شيئا

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

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

يفحص XLSDecodeVectorScene الترويسة وأحجام السجلات والمجاميع وموضع نهاية الملف في البداية، ثم يرفض سجلات escape ويعد سجلات الرسم غير المدعومة
الفحوص البنيوية تجري في البداية ودلالات الفشل بالإغلاق ترفض سجلات escape، بينما سجلات الرسم غير المدعومة تعد وتتخطى فقط

الميزانيات جزء من عقد الصيغة

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

// من lxVectorScene: ميزانية الفك، مصرح بها لا مضمرة
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;

اثنان من هذه يستحقان ملاحظة. سقف عمق سياق الجهاز البالغ 32 موجود لأن سجلات SaveDC وRestoreDC تتداخل، ومسار غير متوازن يمكن أن يدفع إلى الأبد؛ و32 سخية للملفات التعريفية الحقيقية ورخيصة الفرض. وسقف الإحداثيات موجود لأن الإحداثيات تغذي تحويلا، والقيمة قرب حدود المدى الصحيح تنتج نتيجة محولة لا نهائية أو ملتفة، وبعدها يكون كل حساب صندوق محيط لاحقا كلاما بلا معنى. وتثبيت الإحداثيات وقت التحليل أسهل في الاستدلال بكثير من الدفاع عن كل مستهلك للهندسة

ثوابت ميزانية الفك في lxVectorScene في HotXLS للسجلات والمقابض وعمق سياق الجهاز والأوامر والنقاط والنص وحجم الصورة وتثبيت الإحداثيات
كل حد ثابت مسمى يفرض أثناء التحليل؛ وسقف عمق سياق الجهاز وتثبيت الإحداثيات يستحقان أكثر انتباه

استخدام المشهد

يسلّم المفكك كائنا تملكه، وعدد أوامر، وحجما اسميا، وعدد سجلات رسم اختار ألا يمثلها

uses
  lxVectorScene;

var
  Scene: TXLSVectorScene;
  Error: WideString;
  I: Integer;
begin
  // تحمل Data حمولة الصورة الخام المأخوذة من المصنف
  if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
  begin
    // مرفوض: ترويسة أو حدود أو مجاميع أو موضع نهاية الملف أو ميزانية
    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;

سجل الأمر يحمل كل ما يحتاجه مسار خلفي ولا شيء يتطلب جهازا: حضور القلم ولونه وعرضه ونمطه؛ وحضور الفرشاة ولونها؛ والهندسة؛ وللنص السلسلة واسم الخط والحجم والأنماط والمحاذاة. ذلك ما يجعل المشهد نفسه قابلا للاستخدام من مصيّر لوحة الرسم على الشاشة وكاتب SVG معا، وهو سبب عدم انحراف مسار المتجهات بين المعاينة والتصدير. والتصيير الشاشي لمحتوى أوراق العمل عموما مشمول في مقال تصيير شبكة VCL المخصصة

رفض صورة لا يضر المصنف

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

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

ماذا يترك هذا لنشر خادمي

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

يعالج HotXLS صيغ XLS وXLSX وODS وCSV أصليا في Delphi وC++Builder دون Excel مثبتا، ويفوق فلسفة التحليل المحدود نفسها طبقات الحاوية والصيغ والرسم لديه. وتفاصيل الصيغ والأمان مدرجة في صفحة منتج HotXLS Delphi spreadsheet component