Technical Article

Parsing PDF Sicuro per la Memoria: Difendersi dai Documenti Dannosi

I documenti PDF sono incredibilmente potenti, ma questo potere porta con sé intrinseci rischi per la sicurezza. Poiché i PDF supportano file incorporati, JavaScript interattivo e flussi binari complessi, vengono frequentemente utilizzati come vettori per la distribuzione di malware. Buffer overflow, letture fuori limite e integer overflow in parser PDF scritti male possono portare all'esecuzione di codice remoto (RCE).

Se stai sviluppando un'applicazione in Delphi che accetta PDF caricati dagli utenti (ad esempio, un portale di acquisizione documenti), garantire un'analisi PDF sicura per la memoria è un requisito di sicurezza critico.

Vettori di Attacco PDF Comuni

I PDF dannosi di solito prendono di mira le vulnerabilità nel parser stesso piuttosto che nel sistema operativo. Le tecniche più comuni includono:

  • Tabelle di Riferimenti Incrociati (XRef) Malformate: creazione di offset di puntatori che conducono fuori limite, mandando in crash il parser o consentendo la divulgazione della memoria.
  • Cicli Infiniti: creazione di riferimenti circolari tra gli oggetti PDF (ad es. l'Oggetto A fa riferimento all'Oggetto B, che a sua volta fa riferimento all'Oggetto A) portando all'esaurimento dello stack.
  • Decompressione Esplosiva (Zip Bomb): flussi FlateDecode che da pochi kilobyte si decomprimono in gigabyte, esaurendo la memoria di sistema.

Strategie di Parsing Difensive in Delphi

Quando si analizzano i PDF nativamente in Delphi, è necessario programmare sulla difensiva. Non puoi fidarti dei metadati forniti nei dizionari PDF.

1. Interrompere i Riferimenti Circolari

Quando si percorre in modo ricorsivo l'albero degli oggetti PDF, è necessario mantenere una cronologia degli oggetti visitati per prevenire cicli infiniti.

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. Protezione Contro le Zip Bomb

Quando si applica il filtro FlateDecode per decomprimere un flusso, è necessario limitare rigorosamente la dimensione massima di espansione. Non allocare mai la memoria ciecamente in base alla chiave del dizionario /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;

Sfruttare Motori Rinforzati e Componenti Sicuri

Scrivere da zero un parser PDF completamente sicuro è un'impresa titanica. L'approccio standard del settore consiste nell'utilizzare un motore rinforzato e ampiamente testato tramite fuzzing come PDFium, oppure affidarsi a librerie native rigorosamente testate.

PDFium è il motore di rendering principale utilizzato da Google Chrome. Poiché Chrome elabora quotidianamente milioni di PDF non attendibili, PDFium è sottoposto a un fuzzing aggressivo e continuo da parte del Project Zero di Google. Gestisce XRef malformati, flussi interrotti e riferimenti ciclici in modo impeccabile.

Allo stesso modo, componenti nativi come l'HotPDF Component e la Delphi PDF Library integrano di default solide strategie di parsing difensivo. Implementano controlli rigorosi dei limiti, limitatori di profondità ricorsiva e meccanismi di prevenzione delle perdite di memoria progettati specificamente per gli ambienti Delphi e C++Builder.

Sia che tu scelga di consumare PDFium tramite un wrapper Delphi per il rendering, sia che tu utilizzi componenti nativi come HotPDF per la generazione e l'elaborazione di documenti, erediti un perimetro di sicurezza di livello enterprise, proteggendo i tuoi utenti e i tuoi server da payload dannosi senza dover scrivere tu stesso parser difensivi.

Nota: Funzionalità di parsing sicure e testate tramite fuzzing sono disponibili in tutta la nostra suite, inclusi l'HotPDF Component, la Delphi PDF Library e il PDFium Component.