Dokumenti PDF so neverjetno zmogljivi, vendar pa ta zmogljivost prinaša tudi inherentna varnostna tveganja. Ker datoteke PDF podpirajo vdelane datoteke, interaktivni JavaScript in kompleksne binarne tokove, se pogosto uporabljajo kot vektorji za dostavo zlonamerne programske opreme. Prekoračitve medpomnilnika (buffer overflows), branja izven meja (out-of-bounds reads) in prekoračitve celih števil (integer overflows) v slabo napisanih razčlenjevalnikih PDF lahko vodijo do oddaljenega izvajanja kode (RCE).
Če v Delphiju gradite aplikacijo, ki sprejema PDF datoteke, ki jih naložijo uporabniki (npr. portal za sprejem dokumentov), je zagotavljanje pomnilniško varnega razčlenjevanja PDF kritična varnostna zahteva.
Pogosti vektorji napadov prek PDF
Zlonamerne PDF datoteke običajno ciljajo na ranljivosti v samem razčlenjevalniku (parser) in ne v operacijskem sistemu. Pogoste tehnike vključujejo:
- Nepravilno oblikovane tabele navzkrižnih referenc (XRef): Izdelava zamikov kazalcev (pointer offsets), ki vodijo izven meja, kar povzroči sesutje razčlenjevalnika ali omogoči razkritje pomnilnika.
- Neskončne zanke: Ustvarjanje krožnih referenc med objekti PDF (npr. objekt A se sklicuje na objekt B, ta pa se sklicuje na objekt A), kar vodi do izčrpanosti sklada (stack exhaustion).
- Eksplozivna dekompresija (Zip Bombs): Tokovi z aplikacijo FlateDecode, ki se iz nekaj kilobajtov dekompresirajo v gigabajte, s čimer izčrpajo sistemski pomnilnik.
Strategije defenzivnega razčlenjevanja v Delphiju
Pri izvornem razčlenjevanju datotek PDF v Delphiju morate programirati defenzivno. Metapodatkom, ki so podani v slovarjih PDF, ne smete zaupati.
1. Prekinitev krožnih referenc
Pri rekurzivnem sprehajanju po drevesu objektov PDF morate ohraniti zgodovino obiskanih objektov, da preprečite neskončne zanke.
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. Zaščita pred datotekami tipa Zip Bomb
Ko uporabite filter FlateDecode za dekompresijo toka, morate strogo omejiti največjo dovoljeno velikost razširitve. Nikoli ne dodeljujte pomnilnika slepo na podlagi ključa v slovarju `/Length`.
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;
Izkoriščanje utrjenih mehanizmov in varnih komponent
Pisanje popolnoma varnega razčlenjevalnika PDF od začetka je monumentalna naloga. Standardni pristop v industriji je uporaba utrjenega, močno fuzz-testiranega mehanizma, kot je PDFium, ali zanašanje na strogo preizkušene izvorne knjižnice.
PDFium je osnovni mehanizem za upodabljanje, ki ga uporablja Google Chrome. Ker Chrome dnevno obdela milijone nezanesljivih datotek PDF, je PDFium podvržen agresivnemu in nenehnemu testiranju fuzzing s strani Googlove skupine Project Zero. Elegantno obravnava napačno oblikovane XRef-e, prekinjene tokove in ciklične reference.
Podobno izvorne komponente, kot sta HotPDF Component in Delphi PDF Library, že privzeto vključujejo robustne obrambne strategije razčlenjevanja. Implementirajo strogo preverjanje meja, omejevalnike rekurzivne globine in mehanizme za preprečevanje uhajanja pomnilnika, zasnovane posebej za okolji Delphi in C++Builder.
Ne glede na to, ali se odločite za uporabo PDFium prek ovoja Delphi za upodabljanje ali uporabite izvorne komponente, kot je HotPDF, za generiranje in obdelavo dokumentov, podedujete varnostni obod na ravni podjetja, ki ščiti vaše uporabnike in strežnike pred zlonamernimi bremeni, ne da bi vam bilo treba samim pisati obrambne razčlenjevalnike.
Opomba: Varne, fuzz-testirane zmožnosti razčlenjevanja so na voljo v celotnem našem paketu, vključno s HotPDF Component, Delphi PDF Library in PDFium Component.