Artikel Teknis

Memutar EMF dan WMF Tak Tepercaya dengan Aman di HotXLS

Workbook Excel bisa membawa gambar EMF dan WMF, dan cara konvensional menggambarnya adalah menyerahkan stream byte ke pemutar metafile sistem operasi. Itu keputusan yang layak dilihat secara langsung: metafile adalah stream perintah terserialisasi untuk sebuah graphics API, dan memutarnya kembali berarti membiarkan berkas yang datang lewat email mengemudikan graphics driver. HotXLS mengambil rute lain. XLSDecodeVectorScene mem-parse metafile itu sendiri, memvalidasi header, setiap ukuran rekaman, total rekaman yang dideklarasikan, dan penempatan persis rekaman akhir berkas, menolak rekaman escape bulat-bulat, dan mengembalikan TXLSVectorScene berisi perintah menggambar primitif yang diputar ulang oleh backend Canvas dan SVG melalui kode mereka sendiri. Tidak ada playback driver yang terlibat di titik mana pun

HotXLS mem-parse byte worksheet EMF dan WMF tak tepercaya dengan XLSDecodeVectorScene menjadi daftar perintah TXLSVectorScene alih-alih playback metafile GDI
HotXLS mem-parse metafile itu sendiri dan mengembalikan perintah primitif untuk pemutaran ulang Canvas dan SVG; rute konvensional mengeksekusi stream byte pada stack grafik

Pertukarannya adalah cakupan demi penahanan. Whitelist perintah berorientasi persegi tidak akan mereproduksi setiap metafile yang bisa dibuat seorang desainer, sehingga scene melaporkan berapa rekaman menggambar yang tidak dapat ia wakili dan caller memutuskan apa yang harus dilakukan. Untuk proses server yang merender dokumen yang tidak ia buat, pertukaran itu adalah yang benar

Mengapa playback metafile tidak cocok untuk input tak tepercaya?

Karena formatnya bukan gambar, melainkan program. Stream rekaman EMF memanipulasi stack status device-context, mengalokasikan dan memilih objek dari tabel handle, dan bisa membawa rekaman escape yang payload-nya diteruskan ke device driver. Memutarnya kembali menjalankan jalur-jalur di stack grafik platform yang ditulis dengan asumsi metafile datang dari aplikasi yang bekerja sama di mesin yang sama. Ketika inputnya lampiran spreadsheet, asumsi itu hilang, dan sebanyak apa pun kehati-hatian di dalam pustaka spreadsheet tidak menolong karena pustaka bukan komponen yang melakukan parsing

Ini penalaran yang sama yang mengatur lapisan kontainer. Workbook adalah arsip ZIP, dan HotXLS memvalidasi central directory-nya alih-alih mempercayai offset yang dideklarasikan, seperti dijelaskan dalam artikel validasi ZIP end-of-central-directory. Payload metafile adalah lapisan berikutnya dari masalah yang sama

Apa yang diperiksa dekoder sebelum menggambar apa pun

Validasinya struktural dan terjadi di awal, karena parser yang mulai menggambar dan memvalidasi sambil jalan sudah bertindak atas data yang belum diverifikasinya. Header harus cocok secara ketat, bukan sekadar masuk akal. Setiap rekaman harus mendeklarasikan ukuran yang muat di dalam buffer yang tersisa dan cukup besar untuk field tetapnya sendiri. Jumlah rekaman yang dideklarasikan header harus cocok dengan rekaman yang benar-benar ada. Rekaman akhir berkas harus duduk tepat di tempat stream berakhir, bukan sekadar di dekatnya, yang menutup trik trailing-garbage yang menyembunyikan payload kedua di balik gambar yang valid

Di luar struktur, dekoder bersifat fail-closed pada semantik. Rekaman escape ditolak, bukan dilewati. Rekaman pengubah status yang tidak dimodelkan dekoder membuat dekode gagal alih-alih diabaikan, karena mengabaikan perubahan status berarti setiap perintah menggambar berikutnya dieksekusi dalam status yang tidak diminta berkas, dan hasilnya gambar yang keliru dengan cara yang tidak bisa diprediksi siapa pun. Rekaman menggambar di luar set perintah yang didukung adalah perkara berbeda: itu dihitung dan dilewati, karena bentuk yang hilang adalah celah yang terlihat dan dapat dilaporkan, bukan korupsi senyap

XLSDecodeVectorScene memeriksa header, ukuran rekaman, total, dan penempatan EOF di awal, lalu menolak rekaman escape dan menghitung rekaman gambar yang tidak didukung
Pemeriksaan struktural berjalan di awal dan semantik fail-closed menolak rekaman escape, sementara rekaman gambar yang tidak didukung hanya dihitung dan dilewati

Budget adalah bagian dari kontrak format

Format vektor punya versi bom dekompresi mereka sendiri. Beberapa kilobyte rekaman bisa mendeklarasikan polyline dengan ratusan juta titik, atau gambar yang dimensi terdeklarasinya berlipat menjadi terabyte. Batas karenanya harus menjadi konstanta eksplisit, bukan apa pun yang kebetulan bisa selamat dijalani mesin

// Dari lxVectorScene: budget dekode, dinyatakan bukan tersirat
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;

Dua di antaranya layak dicatat. Batas kedalaman device-context 32 ada karena rekaman SaveDC dan RestoreDC bersarang, dan stream yang tidak seimbang bisa mendorong selamanya; 32 longgar untuk metafile nyata dan murah ditegakkan. Batas koordinat ada karena koordinat memberi makan sebuah transform, dan nilai mendekati batas rentang integer menghasilkan hasil tertransformasi yang tak terhingga atau wrap, setelah itu setiap komputasi bounding-box di hilir adalah omong kosong. Menjepit koordinat pada saat parse jauh lebih mudah dinalar daripada mempertahankan setiap konsumen geometri

Konstanta budget dekode di lxVectorScene HotXLS untuk rekaman, handle, kedalaman DC, perintah, titik, teks, ukuran gambar, dan penjepitan koordinat
Setiap batas adalah konstanta bernama yang ditegakkan selama parsing; batas kedalaman DC dan penjepit koordinat paling layak mendapat perhatian

Memakai scene

Dekoder menyerahkan kembali objek yang Anda miliki, jumlah perintah, ukuran nominal, dan jumlah rekaman menggambar yang ia pilih untuk tidak diwakili

uses
  lxVectorScene;

var
  Scene: TXLSVectorScene;
  Error: WideString;
  I: Integer;
begin
  // Data memuat payload gambar mentah yang diambil dari workbook
  if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
  begin
    // Ditolak: header, batas, total, penempatan EOF, atau sebuah budget
    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;

Record perintah membawa semua yang dibutuhkan backend dan tidak ada yang mensyaratkan perangkat: keberadaan pen, warna, lebar, dan style; keberadaan brush dan warnanya; geometrinya; dan untuk teks string, nama font, ukuran, style, dan perataannya. Itulah yang membuat scene yang sama dapat dipakai baik oleh renderer canvas di layar maupun penulis SVG, dan itulah mengapa jalur vektor tidak berbeda antara preview dan ekspor. Rendering layar konten worksheet secara umum dibahas dalam artikel rendering grid VCL kustom

Menolak gambar tidak merusak workbook

Properti penting dari desain ini adalah dekode yang ditolak hanya memengaruhi rendering. Payload asli tetap di dalam model, sehingga workbook yang dibuka dan disimpan lagi membawa gambar metafilenya keluar byte demi byte, apa pun kemampuan dekoder aman menggambarnya. Jalur raster berbatas yang sudah ada juga tetap tersedia sebagai fallback. Dengan kata lain, parser ketat menggerbangkan apa yang dieksekusi, bukan apa yang dipertahankan, dan itulah distinksi yang membuat perubahan bermotif keamanan bisa dirilis tanpa berubah menjadi perubahan kehilangan data

Penanganan objek menggambar secara umum, termasuk bagian model objek yang selamat dari round-trip tanpa tersentuh, dibahas dalam artikel grafik, gambar, dan drawing

Ke mana ini menempatkan deployment server

Jika Anda merender workbook yang diunggah pengguna dalam sebuah layanan, posisi praktisnya kini dapat dipertahankan: gambar metafile di-parse oleh kode yang bisa diaudit, dibatasi konstanta yang bisa dibaca, dan tidak pernah diserahkan ke graphics driver. Peringatan yang jujur adalah cakupan. Metafile kompleks yang diproduksi tool menggambar akan mengenai penghitung rekaman yang dilewati, dan jawabannya adalah memunculkan penghitung itu alih-alih melebarkan whitelist diam-diam. Gambar yang terrender sebagian dan mengatakannya adalah percakapan dukungan; gambar yang terrender keliru dan tidak mengatakan apa pun adalah laporan bug dari pelanggan

HotXLS menangani XLS, XLSX, ODS, dan CSV secara native di Delphi dan C++Builder tanpa Excel terpasang, dan filosofi parse berbatas yang sama mengalir melalui lapisan kontainer, formula, dan gambarnya. Detail format dan keamanan tercantum di halaman produk HotXLS Delphi spreadsheet component