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.