Technical Article

تجزیه ایمن فایل‌های PDF در حافظه: دفاع در برابر اسناد مخرب

اسناد PDF فوق‌العاده قدرتمند هستند، اما این قدرت با خطرات امنیتی ذاتی همراه است. از آنجا که فایل‌های PDF از فایل‌های تعبیه شده، جاوا اسکریپت تعاملی و جریان‌های باینری پیچیده پشتیبانی می‌کنند، اغلب به عنوان ناقلی برای تحویل بدافزار استفاده می‌شوند. سرریزهای بافر (Buffer overflows)، خواندن‌های خارج از محدوده و سرریزهای اعداد صحیح در تجزیه‌کننده‌های PDF که ضعیف نوشته شده‌اند، می‌تواند منجر به اجرای کد از راه دور (RCE) شود.

اگر در حال ساخت برنامه‌ای در دلفی هستید که فایل‌های PDF آپلود شده توسط کاربر را می‌پذیرد (مثلاً یک پورتال دریافت اسناد)، اطمینان از تجزیه ایمن در حافظه یک نیاز امنیتی حیاتی است.

بردارهای حمله رایج در PDF

فایل‌های PDF مخرب معمولاً به جای سیستم‌عامل، آسیب‌پذیری‌های خود تجزیه‌کننده را هدف قرار می‌دهند. تکنیک‌های رایج عبارتند از:

  • جداول مرجع متقابل (XRef) بدشکل: ایجاد آفست‌های اشاره‌گر که به خارج از محدوده هدایت می‌شوند و باعث خرابی تجزیه‌کننده یا امکان افشای حافظه می‌گردند.
  • حلقه‌های بی‌نهایت: ایجاد مراجع دایره‌ای بین اشیاء PDF (به عنوان مثال، شیء A به شیء B اشاره می‌کند، که او نیز به شیء A ارجاع می‌دهد) که منجر به پر شدن و اتمام فضای پشته (stack exhaustion) می‌شود.
  • بمب‌های فشرده‌سازی (Zip Bombs): جریان‌های FlateDecode که از چند کیلوبایت به چند گیگابایت استخراج می‌شوند و حافظه سیستم را تخلیه می‌کنند.

استراتژی‌های تجزیه تدافعی در دلفی

هنگام تجزیه بومی فایل‌های PDF در دلفی، باید به صورت تدافعی برنامه‌نویسی کنید. شما نمی‌توانید به متادیتای ارائه شده در دیکشنری‌های PDF اعتماد کنید.

۱. شکستن ارجاعات دایره‌ای

هنگام پیمایش بازگشتی در یک درخت شیء 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;

۲. محافظت در برابر بمب‌های زیپ

هنگام اعمال فیلتر 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، یا تکیه بر کتابخانه‌های بومی (native) است که با دقت آزمایش شده‌اند.

PDFium موتور رندر اصلی است که توسط Google Chrome استفاده می‌شود. از آنجا که Chrome روزانه میلیون‌ها PDF غیرقابل اعتماد را پردازش می‌کند، PDFium در معرض فازینگ تهاجمی و مداوم توسط Project Zero گوگل قرار دارد. این موتور به شکلی بی‌نقص XRef های بدساخت، جریان‌های خراب و ارجاعات چرخه‌ای را مدیریت می‌کند.

به‌طور مشابه، کامپوننت‌های بومی مانند HotPDF Component و Delphi PDF Library از همان ابتدا استراتژی‌های تجزیه تدافعی قدرتمندی را در خود جای داده‌اند. آن‌ها بررسی‌های دقیق مرزی، محدودکننده‌های عمق بازگشتی و مکانیسم‌های جلوگیری از نشت حافظه را که به‌طور خاص برای محیط‌های Delphi و C++Builder طراحی شده‌اند، پیاده‌سازی می‌کنند.

چه تصمیم بگیرید PDFium را از طریق یک wrapper در Delphi برای رندر کردن مصرف کنید، یا از کامپوننت‌های بومی مانند HotPDF برای تولید و پردازش اسناد استفاده کنید، شما یک محیط امنیتی در سطح سازمانی را به ارث می‌برید که کاربران و سرورهای شما را در برابر پی‌لودهای مخرب محافظت می‌کند، بدون اینکه مجبور باشید خودتان تجزیه‌کننده‌های تدافعی بنویسید.

توجه: قابلیت‌های تجزیه امن و آزمایش‌شده با فازینگ در سراسر مجموعه ما از جمله HotPDF Component، Delphi PDF Library و PDFium Component در دسترس هستند.