HotPDF, en riskli üç PDF görüntü filtresini, DCTDecode, JPXDecode ve JBIG2Decode'u, uygulamanızın içinde değil ayrı, kısa ömürlü bir işçi süreç içinde çözebilir. Bunu açan özellik CodecIsolationMode'dur ve pratik etkisi şudur: VCL uygulamanızı çökertecek olan bozuk bir JPEG 2000 kod akışı artık atılabilir bir alt süreci öldürür, ana süreç ise bir durum kodu bildirip devam eder
Bu fark, PDF'lerin gerçekten geldiği yerlerde en çok önem taşır: bir yükleme formu, bir posta ağ geçidi, bir tarama cihazı, bir iş ortağı FTP bırakması. O baytları siz kontrol etmezsiniz ve tarihsel hasarın yaşadığı yer görüntü kodekleridir
Neden tek bir kötü görüntü tüm uygulamayı çökertir?
Çünkü bir görüntü kodeği, saldırgan kontrollü veriler üzerinde neredeyse hiçbir yapısal kontrole geri dönüş kalmadan karmaşık bir durum makinesi çalıştıran bir PDF okuyucunun tek parçasıdır. Baytlar JPEG 2000 veya JBIG2 kod çözücüsüne ulaştığında, çapraz referans tablosu ayrıştırılmış, nesne çözümlenmiş, filtre zinciri açılmıştır ve geride kalan, kaç döşeme, kaç bileşen, örnek başına kaç bit olduğunu söyleyen ham bir kod akışıdır. Oradaki yanlış bir sayı bir ayrıştırma hatası değildir. Sıkı bir kod çözme döngüsü içinde kötü bir tahsis boyutu ya da aralık dışı bir indekstir
Bütçe sınırları yardımcı olur ve zaten olmalıdır. HotPDF, genişlemeyi DecodeBudgetBytes ve DocumentDecodeBudgetBytes ile sınırlar, filtre zincirlerini ise DecodeFilterLimit ve DecodePipelineDepthLimit ile sınırlar; bu sınırların arkasındaki mantık iç içe filtreler ve PDF bombaları için sınırlı kod çözme'de ele alınmıştır. Ama bir bayt bütçesi yalnızca tek bir soruyu yanıtlar: ne kadar çıktıya izin verilir. Kod çözücü herhangi bir çıktı üretmeden önce hata verdiğinde ne olacağını yanıtlayamaz. Bir kod çözme döngüsü içindeki bir erişim ihlali reddedebileceğiniz bir politika ihlali değildir; süreç düzeyinde bir olaydır ve süreç düzeyinde bir olay için tek güvenilir sınırlama farklı bir süreçtir
HotPDF neyi izole eder, neyi etmez?
HotPDF tam olarak üç kodek türünü izole eder; bunlar HPDFCodecIsolation biriminde hckDCT, hckJPX ve hckJBIG2 olarak numaralandırılır. Geri kalan her şey, Flate, LZW, RunLength, ASCII85, CCITT, süreç içinde kalır, çünkü bu kod çözücüler bütçelerle sınırlandırılabilecek kadar basittir ve ilginç arızaların geldiği yer değildir
Taşıma bilinçli olarak dardır. Ana süreç bir tane sınırlı paylaşımlı bellek eşlemesi ayırır, sabit bir THPDFCodecSharedHeader ile sıkıştırılmış girdiyi ve varsa JBIG2 global segmentlerini yazar, işçiyi başlatır ve bekler. İşçi, çözülmüş pikselleri aynı eşlemeye geri yazar ve bir durum sözcüğü ayarlar. Senkronizasyondan çıkabilecek bir boru protokolü yoktur, bulanıklaştırılacak bir serileştirme biçimi yoktur ve başlık bir sihirli değer ile bir sürüm taşır; bu yüzden uyuşmayan bir işçi ikili dosyası yanlış okunmak yerine reddedilir
uses
HPDFDoc, HPDFCodecIsolation;
var
Pdf: THotPDF;
Info: THPDFCodecWorkerInfo;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
// Kapalı-yönde başarısız ol: bu kodekleri asla süreç içinde çözme
Pdf.CodecIsolationMode := cimRequired;
Pdf.CodecWorkerExecutable := 'HotPDFCodecWorker.exe';
Pdf.CodecWorkerTimeoutMilliseconds := 5000; // 1..600000
Pdf.CodecWorkerMemoryLimitBytes := 268435456; // 0 ya da >= 64 MiB
Pdf.DecodeBudgetBytes := 134217728;
if Pdf.LoadFromFile('untrusted-upload.pdf') = 1 then
if Pdf.GetLoadedImageCount > 0 then
begin
Bmp := Pdf.ExtractLoadedImage(0);
try
if Pdf.GetLastCodecWorkerInfo(Info) then
LogCodecOutcome(Info);
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
CodecWorkerExecutable'ı boş bırakın, HotPDF işçiyi kendi çalıştırılabilir dosyanızın yanında, ParamStr(0)'ın dizininde HotPDFCodecWorker.exe olarak çözer. Dağıtımınız işçiyi başka bir yere koyduğunda bunu açıkça ayarlayın; değer ExpandFileName üzerinden genişletilir, bu yüzden göreli bir yol uygulama dizinine değil geçerli dizine göre çözülür ki bu bir serviste nadiren istediğiniz şeydir
Otomatik mi zorunlu mu: hangi başarısızlığı tercih edersiniz?
THPDFCodecIsolationMode'un üç değeri, tek bir soruya üç farklı yanıt kodlar: işçi hiç çalışamadığında ne olmalı. cimDisabled, izolasyonu tamamen atlar ve süreç içinde çözer; bu 3.x öncesi davranıştır. Varsayılan olan cimAutomatic, işçiyi dener ve işçi çalıştırılabilir dosyası eksik olduğunda ya da başlatılamadığında sessizce süreç içi çözmeye geri döner; bu durum cwsUnavailable olarak raporlanır. cimRequired bu geri dönüşü reddeder: kullanılamayan bir işçi, çözmeyi ele alınmış ve başarısız olarak işaretler; bu yüzden hiçbir güvenilmeyen kod akışı adres alanınıza ulaşmaz
Kolaylığa göre değil tehdit modeline göre seçin. Kullanıcının diskte zaten sahip olduğu belgeleri açan bir masaüstü görüntüleyici cimAutomatic ile gayet iyidir; burada eksik bir işçi, ürünü bozmak yerine klasik davranışa geri düşer. İnternetten dosya ayrıştıran bir alım servisi cimRequired çalıştırmalıdır, çünkü izolasyon katmanını sessizce düşüren bir dağıtım hatası, önemli olana kadar kimsenin fark etmediği türden bir gerilemedir. Asimetriye dikkat edin: yalnızca cwsUnavailable geri dönüşü tetikler. Başlayıp sonra çöken, zaman aşımına uğrayan ya da bir sınıra çarpan bir işçi, her iki modda da bir kod çözme başarısızlığıdır, asla süreç içinde sessiz bir yeniden deneme değil
THPDFCodecWorkerStatus'tan sonucu okumak
GetLastCodecWorkerInfo, en son izole kod çözmenin sonucunu döndürür ve durum numaralandırması, genel bir "görüntü başarısız" günlük satırı yerine gerçek operasyonel kararları yönlendirecek kadar spesifiktir. Değerler cwsNotRun, cwsSucceeded, cwsUnavailable, cwsLaunchFailed, cwsTimedOut, cwsCrashed, cwsDecodeFailed, cwsProtocolError ve cwsOutputLimit'tir
Bunları üç grup olarak ele alın. Dağıtım sorunları cwsUnavailable ve cwsLaunchFailed'dir: birisi işçi olmadan gönderim yapmıştır ya da bir antivirüs ürünü süreç oluşturmayı engellemektedir. Belge sorunları cwsDecodeFailed ve cwsOutputLimit'tir: dosya bozuk ya da politikanızın izin verdiğinden büyüktür ve onu reddetmek doğru yanıttır. İlginç grup cwsTimedOut ve cwsCrashed'dir, çünkü bunlar daha önce ana süreci askıda bırakacak ya da öldürecek olaylardır. Bu olduğunda, eşlik eden ProcessId, ExitCode ve ElapsedMilliseconds alanları, bir Windows Hata Bildirimi kaydıyla ilişkilendirmenize ve bir müşteri dosyasının mı patolojik olduğuna yoksa birinin sizi mi yokladığına karar vermenize yetecek kadar bilgi verir
procedure LogCodecOutcome(const Info: THPDFCodecWorkerInfo);
begin
case Info.Status of
cwsSucceeded:
; // raporlanacak bir şey yok
cwsUnavailable, cwsLaunchFailed:
Alert('Codec worker not deployed: ' + Info.ErrorMessage);
cwsTimedOut, cwsCrashed:
Quarantine(Format('pid %d exit %d after %d ms',
[Info.ProcessId, Info.ExitCode, Info.ElapsedMilliseconds]));
else
RejectDocument(Info.ErrorMessage);
end;
end;
Gerçekten bağlayıcı olan sınırlar
Her izole kod çözme için üç ayrı tavan geçerlidir ve hangisinin ateşlendiğini bilmek bir öğleden sonralık tahmin oyunundan kurtarır. CodecWorkerTimeoutMilliseconds varsayılan olarak 10.000'dir ve 1 ile 600.000 aralığına doğrulanır; aralık dışı bir değer sessizce kırpılmak yerine hata verir. CodecWorkerMemoryLimitBytes varsayılan olarak 536.870.912 bayttır ve ya sıfır, yani sınır yok, ya da en az 67.108.864 bayt olmalıdır, çünkü daha küçük bir tavan gerçekçi bir kod çözücü çalışma kümesini tutamaz ve her belgeyi başarısız kılar. Bellek tavanı, kapatınca-öldür anlamına sahip bir Windows İş Nesnesi tarafından uygulanır; bu yüzden işçi, ana süreç aniden sonlandırılsa bile işle birlikte ölür
Üçüncü tavan çıktı sınırıdır ve yapılandırılmak yerine türetilir. HotPDF, gereken baytları istenen bölgeden ya da beklenen görüntü geometrisinden, 24 bit çıktı için genişlik çarpı yükseklik çarpı üç olarak hesaplar, ardından bir bütçe ayarlıysa bu değeri DecodeBudgetBytes'a kırpar. Makul bir başlık raporlayıp sonra geometrinin izin verdiğinden çok daha fazla piksel yaymaya çalışan bir kod çözücü, eşlemenin kendisi tarafından durdurulur ve ana süreç cwsOutputLimit görür. İzolasyon katmanı ile kod çözme bütçesinin birbirini tamamlamasının nedeni budur: bütçe bir görüntünün ne kadar büyük olmasına izin verildiğini tanımlar, izolasyon sınırı ise o boyut hakkındaki bir yalanın sürecinizde sınır dışı bir yazmaya dönüşemeyeceğinden emin olur
Bu, sertleştirilmiş bir alım yolunda nereye oturur?
Süreç izolasyonu, çok daha önce başlayan bir savunma zincirinin en dış katmanıdır. Yapısal sınırlar, ayrıştırma anında olası olmayan belgeleri reddeder. Filtre bütçeleri genişlemeyi sınırlar. İzolasyon, her ikisinden de sağ çıkanı kapsar. Görüntü katmanına ulaşan belgeler için, gerçekte hangi kodeği çalıştırdığınızı bilmekte fayda var, çünkü JPXDecode işleme ile JBIG2 sembol sözlükleri çok farklı başarısızlık profillerine sahiptir ve özellikle JBIG2, saf bir görüntü başına korumanın bozacağı sayfalar arası global segmentler taşır
Maliyet dürüsttür ve belirtmeye değer: izole edilen görüntü başına bir süreç başlatmak milisaniyeler ekler ve yüzlerce taranmış sayfalı bir belge bunu hissedecektir. Bunu satın aldığına karşı ölçün. Denetimsiz çalışan bir gece toplu dönüştürücüde, verim kaybı görünmez ve çökme sınırlaması işin tüm noktasıdır. Kullanıcının zaten güvendiği belgeleri açan etkileşimli bir görüntüleyicide cimDisabled ya da cimAutomatic makul varsayılandır. Mod sade bir özelliktir; bu yüzden hiçbir şey, çalışma zamanında belge sınıfı başına seçim yapmanızı engellemez
HotPDF, izolasyon katmanını, kod çözme bütçelerini ve yapısal ayrıştırıcı sınırlarını, işçi çalıştırılabilir dosyasının ötesinde dağıtılacak harici bir çalışma zamanı olmadan, Delphi ve C++Builder için tek bir yerel VCL bileşeni olarak sunar. Tam API belgeleri ve bir deneme sürümü HotPDF Delphi PDF bileşeni sayfasında bulunur