Technical Article

Безпечний для пам'яті аналіз PDF: Захист від зловмисних документів

PDF-документи є неймовірно потужними, але ця потужність супроводжується притаманними ризиками безпеки. Оскільки PDF-файли підтримують вбудовані файли, інтерактивний JavaScript і складні двійкові потоки, вони часто використовуються як вектори для доставки шкідливого програмного забезпечення. Переповнення буфера, читання за межами пам'яті (out-of-bounds) та цілочисельні переповнення в погано написаних аналізаторах PDF можуть призвести до віддаленого виконання коду (RCE).

Якщо ви створюєте додаток на Delphi, який приймає завантажені користувачами PDF-файли (наприклад, портал для прийому документів), забезпечення безпечного для пам'яті аналізу PDF є критичною вимогою безпеки.

Поширені вектори атак на PDF

Зловмисні PDF-файли зазвичай націлені на вразливості в самому аналізаторі, а не в операційній системі. Поширені методи включають:

  • Неправильно сформовані таблиці перехресних посилань (XRef): Створення зміщень вказівників, які виходять за межі, що призводить до збою аналізатора або дозволяє розкрити вміст пам'яті.
  • Нескінченні цикли: Створення циклічних посилань між об'єктами PDF (наприклад, об'єкт A посилається на об'єкт B, який посилається на об'єкт A), що призводить до вичерпання стека.
  • Вибухова декомпресія (Zip Bombs): Потоки FlateDecode, які розпаковуються з кількох кілобайт у гігабайти, вичерпуючи системну пам'ять.

Стратегії захисного аналізу в Delphi

Під час нативного аналізу PDF-файлів у Delphi ви повинні програмувати з урахуванням захисту. Ви не можете довіряти метаданим, наданим у словниках PDF.

1. Розрив циклічних посилань

При рекурсивному обході дерева об'єктів PDF ви повинні підтримувати історію відвіданих об'єктів, щоб запобігти нескінченним циклам.

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 Bombs

Застосовуючи фільтр FlateDecode для розпакування потоку, ви повинні суворо обмежити максимальний розмір розширення. Ніколи не виділяйте пам'ять наосліп на основі ключа словника /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;

Використання посилених рушіїв та безпечних компонентів

Написання повністю безпечного парсера PDF з нуля є монументальним завданням. Стандартним галузевим підходом є використання посиленого рушія, що пройшов ретельне фаззинг-тестування, такого як PDFium, або покладання на суворо перевірені нативні бібліотеки.

PDFium є основним рушієм рендерингу, який використовується в Google Chrome. Оскільки Chrome щодня обробляє мільйони ненадійних PDF-файлів, PDFium піддається агресивному, безперервному фаззингу з боку Project Zero від Google. Він витончено обробляє неправильно сформовані XRef, пошкоджені потоки та циклічні посилання.

Аналогічно, нативні компоненти, такі як HotPDF Component та Delphi PDF Library, включають надійні стратегії захисного парсингу "з коробки". Вони реалізують сувору перевірку меж, обмежувачі глибини рекурсії та механізми запобігання витоку пам'яті, спеціально розроблені для середовищ Delphi та C++Builder.

Незалежно від того, чи виберете ви використання PDFium через оболонку Delphi для рендерингу, чи застосуєте нативні компоненти, такі як HotPDF, для генерації та обробки документів, ви успадковуєте периметр безпеки корпоративного рівня, захищаючи ваших користувачів і сервери від шкідливих навантажень без необхідності самостійно писати захисні парсери.

Примітка: Безпечні можливості парсингу, перевірені фаззингом, доступні в усьому нашому наборі, включаючи HotPDF Component, Delphi PDF Library та PDFium Component.