Technical Article

Geheugenveilig PDF-parsen: Verdedigen tegen schadelijke documenten

PDF-documenten zijn ongelooflijk krachtig, maar die kracht brengt inherente veiligheidsrisico's met zich mee. Omdat PDF's ingesloten bestanden, interactieve JavaScript en complexe binaire streams ondersteunen, worden ze vaak gebruikt als vectoren voor de distributie van malware. Buffer overflows, out-of-bounds reads en integer overflows in slecht geschreven PDF-parsers kunnen leiden tot uitvoering van externe code (RCE).

Als u een applicatie in Delphi bouwt die door gebruikers geüploade PDF's accepteert (bijv. een portaal voor documentopname), is het garanderen van geheugenveilig PDF-parsen een cruciale beveiligingsvereiste.

Veelvoorkomende PDF-aanvalsvectoren

Kwaadaardige PDF's richten zich doorgaans op kwetsbaarheden in de parser zelf in plaats van in het besturingssysteem. Veelvoorkomende technieken zijn onder meer:

  • Misvormde kruisverwijzingstabellen (XRef): Het maken van pointer-offsets die buiten de grenzen (out-of-bounds) leiden, waardoor de parser crasht of geheugenblootlegging mogelijk wordt.
  • Oneindige lussen: Het creëren van circulaire referenties tussen PDF-objecten (bijv. Object A verwijst naar Object B, dat verwijst naar Object A), wat leidt tot uitputting van de stack.
  • Exploderende decompressie (Zip-bommen): FlateDecode-streams die decomprimeren van een paar kilobytes naar gigabytes, waardoor het systeemgeheugen uitgeput raakt.

Defensieve parsingstrategieën in Delphi

Bij het native parsen van PDF's in Delphi moet u defensief programmeren. U kunt de metadata in de PDF-dictionaries niet vertrouwen.

1. Circulaire referenties doorbreken

Wanneer u recursief een PDF-objectboom doorloopt, moet u een geschiedenis bijhouden van bezochte objecten om oneindige lussen te voorkomen.

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. Beschermen tegen zip-bommen

Wanneer u het FlateDecode-filter toepast om een stream te decomprimeren, moet u de maximale uitbreidingsgrootte strikt beperken. Wijs nooit blindelings geheugen toe op basis van de dictionariesleutel /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;

Gebruikmaken van Geharde Engines en Veilige Componenten

Het schrijven van een volledig veilige PDF-parser vanaf nul is een enorme taak. De standaardbenadering in de industrie is het gebruik van een geharde, zwaar met fuzzing geteste engine zoals PDFium, of het vertrouwen op streng geteste native bibliotheken.

PDFium is de kern-renderingengine die wordt gebruikt door Google Chrome. Omdat Chrome dagelijks miljoenen onbetrouwbare PDF's verwerkt, wordt PDFium onderworpen aan agressieve, continue fuzzing door Google's Project Zero. Het verwerkt misvormde XRefs, kapotte streams en cyclische verwijzingen op een elegante manier.

Evenzo bevatten native componenten zoals het HotPDF Component en de Delphi PDF Library standaard robuuste defensieve parsingstrategieën. Ze implementeren strikte grenscontroles, recursieve dieptebeperkingen en mechanismen ter voorkoming van geheugenlekken die speciaal zijn ontworpen voor Delphi- en C++Builder-omgevingen.

Of u er nu voor kiest om PDFium via een Delphi-wrapper te gebruiken voor rendering, of native componenten zoals HotPDF gebruikt voor het genereren en verwerken van documenten, u erft een beveiligingsperimeter op ondernemingsniveau, waardoor uw gebruikers en uw servers worden beschermd tegen schadelijke payloads zonder dat u zelf defensieve parsers hoeft te schrijven.

Opmerking: Veilige, met fuzzing geteste parsingmogelijkheden zijn beschikbaar in onze hele suite, inclusief het HotPDF Component, de Delphi PDF Library en het PDFium Component.