PDF-документы невероятно мощны, но эта мощь сопряжена с неотъемлемыми рисками для безопасности. Поскольку PDF поддерживают встроенные файлы, интерактивный JavaScript и сложные бинарные потоки, они часто используются как векторы для доставки вредоносного ПО. Переполнения буфера, чтение за пределами границ (out-of-bounds reads) и целочисленные переполнения в плохо написанных парсерах PDF могут привести к удаленному выполнению кода (RCE).
Если вы создаете приложение в Delphi, которое принимает загружаемые пользователями PDF (например, портал приема документов), обеспечение безопасности памяти при синтаксическом анализе PDF является критическим требованием безопасности.
Общие векторы атак через PDF
Вредоносные PDF обычно нацелены на уязвимости в самом парсере, а не в операционной системе. Распространенные методы включают в себя:
- Неправильно сформированные таблицы перекрестных ссылок (XRef): создание смещений указателей, которые ведут за пределы допустимых границ, вызывая сбой парсера или допуская раскрытие памяти.
- Бесконечные циклы: создание циклических ссылок между объектами PDF (например, Объект A ссылается на Объект B, который ссылается на Объект A), что приводит к исчерпанию стека.
- Взрывная распаковка (Zip Bombs): потоки FlateDecode, которые распаковываются с нескольких килобайт до гигабайт, исчерпывая системную память.
Стратегии защитного парсинга в Delphi
При нативном парсинге PDF в Delphi вы должны программировать с точки зрения защиты (defensively). Вы не можете доверять метаданным, указанным в словарях 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-бомб
При применении фильтра 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 подвергается агрессивному и непрерывному фаззингу со стороны Google Project Zero. Он корректно обрабатывает поврежденные XRef, неисправные потоки и циклические ссылки.
Аналогично, нативные компоненты, такие как HotPDF Component и Delphi PDF Library, по умолчанию включают надежные стратегии защитного парсинга. Они реализуют строгую проверку границ, ограничители глубины рекурсии и механизмы предотвращения утечек памяти, разработанные специально для сред Delphi и C++Builder.
Независимо от того, решите ли вы использовать PDFium через обертку Delphi для рендеринга или будете использовать нативные компоненты, такие как HotPDF, для генерации и обработки документов, вы унаследуете периметр безопасности корпоративного уровня, защищающий ваших пользователей и ваши серверы от вредоносных полезных нагрузок без необходимости писать защитные парсеры самостоятельно.
Примечание: Безопасные возможности парсинга, протестированные фаззингом, доступны во всем нашем наборе продуктов, включая HotPDF Component, Delphi PDF Library и PDFium Component.