Technical Article

Speichersicheres PDF-Parsing: Verteidigung gegen bösartige Dokumente

PDF-Dokumente sind unglaublich leistungsfähig, aber diese Leistungsfähigkeit bringt inhärente Sicherheitsrisiken mit sich. Da PDFs eingebettete Dateien, interaktives JavaScript und komplexe binäre Streams unterstützen, werden sie häufig als Vektoren für die Verbreitung von Malware verwendet. Pufferüberläufe, Out-of-Bounds-Lesevorgänge und Integer-Überläufe in schlecht geschriebenen PDF-Parsern können zur Remotecodeausführung (RCE) führen.

Wenn Sie eine Anwendung in Delphi erstellen, die von Benutzern hochgeladene PDFs akzeptiert (z. B. ein Dokumentenerfassungsportal), ist die Gewährleistung eines speichersicheren PDF-Parsings eine kritische Sicherheitsanforderung.

Häufige PDF-Angriffsvektoren

Bösartige PDFs zielen in der Regel auf Schwachstellen im Parser selbst ab und nicht auf das Betriebssystem. Gängige Techniken umfassen:

  • Fehlerhafte Kreuzreferenztabellen (XRef): Erstellung von Zeiger-Offsets, die über die Grenzen hinausführen und den Parser zum Absturz bringen oder die Offenlegung des Speichers ermöglichen.
  • Endlosschleifen: Erstellung von zirkulären Referenzen zwischen PDF-Objekten (z. B. referenziert Objekt A das Objekt B, welches wiederum Objekt A referenziert), was zur Erschöpfung des Stacks (Stack Exhaustion) führt.
  • Explodierende Dekomprimierung (Zip-Bomben): FlateDecode-Streams, die von wenigen Kilobyte auf Gigabyte anwachsen und den Systemspeicher erschöpfen.

Defensive Parsing-Strategien in Delphi

Beim nativen Parsen von PDFs in Delphi müssen Sie defensiv programmieren. Sie können den in den PDF-Wörterbüchern bereitgestellten Metadaten nicht vertrauen.

1. Unterbrechen von zirkulären Referenzen

Wenn Sie einen PDF-Objektbaum rekursiv durchlaufen, müssen Sie einen Verlauf der besuchten Objekte führen, um Endlosschleifen zu vermeiden.

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. Schutz vor Zip-Bomben

Wenn Sie den Filter FlateDecode zum Dekomprimieren eines Streams anwenden, müssen Sie die maximale Expansionsgröße strikt begrenzen. Weisen Sie niemals blind Speicher basierend auf dem Wörterbuchschlüssel `/Length` zu.

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;

Nutzung gehärteter Engines und sicherer Komponenten

Einen vollständig sicheren PDF-Parser von Grund auf neu zu schreiben, ist eine monumentale Aufgabe. Der branchenübliche Ansatz besteht darin, eine gehärtete, stark fuzz-getestete Engine wie PDFium zu verwenden oder sich auf streng getestete native Bibliotheken zu verlassen.

PDFium ist die von Google Chrome verwendete Kern-Rendering-Engine. Da Chrome täglich Millionen nicht vertrauenswürdiger PDFs verarbeitet, wird PDFium von Googles Project Zero aggressivem und kontinuierlichem Fuzzing unterzogen. Es geht elegant mit fehlerhaften XRefs, beschädigten Streams und zyklischen Referenzen um.

Ebenso integrieren native Komponenten wie die HotPDF Component und die Delphi PDF Library von Haus aus robuste defensive Parsing-Strategien. Sie implementieren strenge Grenzwertprüfungen, rekursive Tiefenbegrenzer und Mechanismen zur Vermeidung von Speicherlecks, die speziell für Delphi- und C++Builder-Umgebungen entwickelt wurden.

Unabhängig davon, ob Sie PDFium über einen Delphi-Wrapper für das Rendering nutzen oder native Komponenten wie HotPDF für die Dokumentengenerierung und -verarbeitung verwenden, erben Sie einen Sicherheitsperimeter auf Unternehmensniveau, der Ihre Benutzer und Server vor schädlichen Payloads schützt, ohne dass Sie selbst defensive Parser schreiben müssen.

Hinweis: Sichere, fuzz-getestete Parsing-Funktionen sind in unserer gesamten Suite verfügbar, einschließlich der HotPDF Component, der Delphi PDF Library und der PDFium Component.