Technical Article

Bellek Güvenli PDF Ayrıştırma: Kötü Amaçlı Belgelere Karşı Savunma

PDF belgeleri inanılmaz derecede güçlüdür, ancak bu güç beraberinde doğal güvenlik riskleri getirir. PDF'ler gömülü dosyaları, etkileşimli JavaScript'i ve karmaşık ikili akışları (binary streams) desteklediğinden, sıklıkla kötü amaçlı yazılım (malware) dağıtımı için vektör olarak kullanılırlar. Kötü yazılmış PDF ayrıştırıcılarındaki (parsers) arabellek taşmaları (buffer overflows), sınır dışı okumalar (out-of-bounds reads) ve tamsayı taşmaları (integer overflows) uzaktan kod yürütülmesine (RCE) yol açabilir.

Delphi'de kullanıcı tarafından yüklenen PDF'leri (örneğin, bir belge alma portalı) kabul eden bir uygulama geliştiriyorsanız, bellek açısından güvenli bir PDF ayrıştırması (memory-safe PDF parsing) sağlamak kritik bir güvenlik gereksinimidir.

Yaygın PDF Saldırı Vektörleri

Kötü amaçlı PDF'ler genellikle işletim sisteminden ziyade ayrıştırıcının kendisindeki güvenlik açıklarını hedefler. Yaygın teknikler şunları içerir:

  • Hatalı Biçimlendirilmiş Çapraz Referans (XRef) Tabloları: Sınırların dışına çıkan işaretçi ofsetleri (pointer offsets) oluşturarak ayrıştırıcıyı çökertmek veya bellek ifşasına (memory disclosure) olanak tanımak.
  • Sonsuz Döngüler: Yığın tükenmesine (stack exhaustion) yol açan PDF nesneleri arasında dairesel referanslar (circular references) oluşturmak (örneğin, A Nesnesi, A Nesnesine referans veren B Nesnesine referans verir).
  • Patlayan Sıkıştırma Açma (Zip Bombaları): Birkaç kilobayttan gigabaytlara kadar açılarak sistem belleğini tüketen FlateDecode akışları.

Delphi'de Savunmacı Ayrıştırma Stratejileri

PDF'leri Delphi'de yerel olarak ayrıştırırken, savunmacı (defensive) programlama yapmalısınız. PDF sözlüklerinde (dictionaries) sağlanan meta verilere güvenemezsiniz.

1. Dairesel Referansları Kırmak

Bir PDF nesne ağacında (object tree) yinelemeli (recursively) olarak gezinirken, sonsuz döngüleri önlemek için ziyaret edilen nesnelerin bir geçmişini tutmalısınız.

uses
  System.Generics.Collections, System.SysUtils;

// A safe recursive function to walk the PDF tree
procedure ParsePDFDictionary(DictObj: TPDFDictionary; Visited: TList<Integer>);
var
  ObjID: Integer;
begin
  ObjID := DictObj.ObjectID;
  
  if Visited.Contains(ObjID) then
  begin
    Writeln('Warning: Circular reference detected. Aborting branch.');
    Exit;
  end;
  
  Visited.Add(ObjID);
  
  try
    // Process child objects safely...
  finally
    // Allow siblings to traverse, but prevent vertical recursion loops
    Visited.Remove(ObjID);
  end;
end;

2. Zip Bombalarına Karşı Korunma

Bir akışın sıkıştırmasını açmak için FlateDecode filtresini uygularken, maksimum genişleme boyutunu (expansion size) kesinlikle sınırlamalısınız. /Length sözlük (dictionary) anahtarına dayanarak asla körü körüne bellek ayırmayın.

const
  MAX_DECOMPRESSED_SIZE = 1024 * 1024 * 50; // 50 MB safety limit

procedure DecompressPDFStream(CompressedStream, OutputTarget: TStream);
var
  ZLibStream: TZDecompressionStream;
  Buffer: array[0..8191] of Byte;
  BytesRead, TotalRead: Integer;
begin
  ZLibStream := TZDecompressionStream.Create(CompressedStream);
  try
    TotalRead := 0;
    repeat
      BytesRead := ZLibStream.Read(Buffer[0], SizeOf(Buffer));
      if BytesRead > 0 then
      begin
        TotalRead := TotalRead + BytesRead;
        if TotalRead > MAX_DECOMPRESSED_SIZE then
          raise Exception.Create('Security Exception: Decompression bomb detected!');
          
        OutputTarget.WriteBuffer(Buffer[0], BytesRead);
      end;
    until BytesRead = 0;
  finally
    ZLibStream.Free;
  end;
end;

Güçlendirilmiş Motorlardan ve Güvenli Bileşenlerden Yararlanma

Sıfırdan tamamen güvenli bir PDF ayrıştırıcısı yazmak anıtsal bir görevdir. Endüstri standardı yaklaşım, PDFium gibi güçlendirilmiş, yoğun bir şekilde fuzz testine tabi tutulmuş bir motor kullanmak veya titizlikle test edilmiş yerel kütüphanelere güvenmektir.

PDFium, Google Chrome tarafından kullanılan temel oluşturma motorudur. Chrome her gün milyonlarca güvenilmeyen PDF işlediği için, PDFium Google'ın Project Zero ekibi tarafından agresif ve sürekli fuzzing işlemine tabi tutulur. Hatalı biçimlendirilmiş XRef'leri, bozuk akışları ve döngüsel referansları sorunsuzca işler.

Benzer şekilde, HotPDF Component ve Delphi PDF Library gibi yerel bileşenler, kutudan çıktığı gibi sağlam savunma ayrıştırma stratejilerini bünyesinde barındırır. Delphi ve C++Builder ortamları için özel olarak tasarlanmış katı sınır kontrolü, yinelemeli derinlik sınırlayıcılar ve bellek sızıntısını önleme mekanizmalarını uygularlar.

Oluşturma için bir Delphi sarmalayıcısı aracılığıyla PDFium'u kullanmayı veya belge oluşturma ve işleme için HotPDF gibi yerel bileşenleri kullanmayı seçin; kendi başınıza savunma ayrıştırıcıları yazmak zorunda kalmadan kullanıcılarınızı ve sunucularınızı kötü amaçlı yüklere karşı koruyan kurumsal düzeyde bir güvenlik çevresini miras alırsınız.

Not: Güvenli, fuzz testine tabi tutulmuş ayrıştırma yetenekleri, HotPDF Component, Delphi PDF Library ve PDFium Component dahil olmak üzere tüm paketimiz genelinde mevcuttur.