Technical Article

ניתוח PDF בטוח בזיכרון (Memory-Safe): הגנה מפני מסמכים זדוניים

מסמכי PDF הם עוצמתיים להפליא, אך עוצמה זו מלווה בסיכוני אבטחה מובנים. מכיוון שקובצי PDF תומכים בקבצים מוטמעים, ב-JavaScript אינטראקטיבי ובזרמים בינאריים מורכבים, הם משמשים לעתים קרובות כווקטורים להעברת תוכנות זדוניות (malware). גלישות חוצץ (buffer overflows), קריאות מחוץ לגבולות (out-of-bounds reads) וגלישות מספרים שלמים (integer overflows) במנתחי PDF כתובים בצורה גרועה עלולים להוביל להרצת קוד מרחוק (RCE).

אם אתם בונים יישום ב-Delphi המקבל קובצי PDF המועלים על ידי משתמשים (למשל, פורטל לקליטת מסמכים), הבטחת ניתוח PDF בטוח בזיכרון (memory-safe PDF parsing) היא דרישת אבטחה קריטית.

וקטורי תקיפה נפוצים ב-PDF

מסמכי PDF זדוניים בדרך כלל מכוונים לפגיעויות במנתח עצמו, ולא במערכת ההפעלה. טכניקות נפוצות כוללות:

  • טבלאות XRef (Cross-Reference) פגומות: יצירת היסטים (offsets) של מצביעים שמובילים מחוץ לגבולות, מה שגורם לקריסת המנתח או מאפשר חשיפת זיכרון.
  • לולאות אינסופיות: יצירת הפניות מעגליות בין אובייקטים של PDF (למשל, אובייקט A מפנה לאובייקט B, אשר מפנה חזרה לאובייקט A) המובילות למיצוי המחסנית (stack exhaustion).
  • פיצוץ פריסה (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 מאובטח לחלוטין מאפס היא משימה כבירה. הגישה הסטנדרטית בתעשייה היא להשתמש במנוע מוקשח שנבדק בקפדנות באמצעות fuzzing כמו PDFium, או להסתמך על ספריות מקומיות (native) שנבדקו בקפידה.

PDFium הוא מנוע הרינדור המרכזי המשמש את Google Chrome. מכיוון ש-Chrome מעבד מדי יום מיליוני קובצי PDF שאינם מהימנים, PDFium נתון ל-fuzzing אגרסיבי ורציף על ידי Project Zero של Google. הוא מטפל ב-XRefs פגומים, זרמים שבורים והפניות מעגליות בצורה חלקה.

באופן דומה, רכיבים מקומיים כמו ה-HotPDF Component וה-Delphi PDF Library משלבים אסטרטגיות ניתוח הגנתיות חזקות היישר מהקופסה. הם מיישמים בדיקת גבולות קפדנית, מגבילי עומק רקורסיביים ומנגנוני מניעת דליפת זיכרון המיועדים במיוחד לסביבות Delphi ו-C++Builder.

בין אם תבחרו לצרוך את PDFium דרך מעטפת (wrapper) של Delphi עבור רינדור, ובין אם תשתמשו ברכיבים מקומיים כמו HotPDF ליצירה ועיבוד של מסמכים, אתם יורשים מעטפת אבטחה ברמה ארגונית, המגנה על המשתמשים והשרתים שלכם מפני מטענים זדוניים (payloads) מבלי שתצטרכו לכתוב מנתחים הגנתיים בעצמכם.

הערה: יכולות ניתוח מאובטחות שנבדקו באמצעות fuzzing זמינות בכל חבילת המוצרים שלנו, לרבות HotPDF Component, Delphi PDF Library, ו-ה-PDFium Component.