Technical Article

Phân tích cú pháp PDF an toàn bộ nhớ: Phòng thủ chống lại các tài liệu độc hại

Tài liệu PDF vô cùng mạnh mẽ, nhưng sức mạnh đó đi kèm với những rủi ro bảo mật vốn có. Bởi vì tệp PDF hỗ trợ các tệp nhúng, JavaScript tương tác và các luồng nhị phân phức tạp, chúng thường được sử dụng làm trung gian truyền phần mềm độc hại. Tràn bộ đệm, đọc ngoài giới hạn và tràn số nguyên trong các trình phân tích cú pháp PDF được viết kém có thể dẫn đến việc thực thi mã từ xa (RCE).

Nếu bạn đang xây dựng một ứng dụng trong Delphi chấp nhận tệp PDF do người dùng tải lên (ví dụ: một cổng tiếp nhận tài liệu), thì việc đảm bảo phân tích cú pháp PDF an toàn bộ nhớ là một yêu cầu bảo mật quan trọng.

Các hướng tấn công PDF phổ biến

Các tệp PDF độc hại thường nhắm mục tiêu vào các lỗ hổng trong chính trình phân tích cú pháp thay vì hệ điều hành. Các kỹ thuật phổ biến bao gồm:

  • Bảng Tham chiếu chéo (XRef) bị định dạng sai: Tạo các phần bù con trỏ (pointer offsets) dẫn ra ngoài giới hạn, làm sập trình phân tích cú pháp hoặc cho phép tiết lộ bộ nhớ.
  • Vòng lặp vô hạn: Tạo tham chiếu vòng (circular references) giữa các đối tượng PDF (ví dụ: Đối tượng A tham chiếu đến Đối tượng B, Đối tượng B tham chiếu trở lại Đối tượng A) dẫn đến cạn kiệt ngăn xếp.
  • Bom giải nén (Zip Bombs): Các luồng FlateDecode giải nén từ vài kilobyte thành hàng gigabyte, làm cạn kiệt bộ nhớ hệ thống.

Chiến lược phân tích cú pháp phòng thủ trong Delphi

Khi phân tích cú pháp gốc các tệp PDF trong Delphi, bạn phải lập trình một cách phòng thủ. Bạn không thể tin tưởng vào siêu dữ liệu được cung cấp trong từ điển PDF.

1. Phá vỡ tham chiếu vòng

Khi duyệt đệ quy cây đối tượng PDF, bạn phải duy trì lịch sử các đối tượng đã truy cập để ngăn chặn các vòng lặp vô hạn.

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. Phòng chống Zip Bombs

Khi áp dụng bộ lọc FlateDecode để giải nén một luồng, bạn phải giới hạn nghiêm ngặt kích thước mở rộng tối đa. Không bao giờ phân bổ bộ nhớ một cách mù quáng dựa trên khóa từ điển /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;

Tận dụng các Engine được tăng cường và Component an toàn

Viết một trình phân tích cú pháp PDF hoàn toàn bảo mật từ đầu là một nhiệm vụ khổng lồ. Cách tiếp cận tiêu chuẩn của ngành là sử dụng một engine được tăng cường, được kiểm thử fuzzing nghiêm ngặt như PDFium, hoặc dựa vào các thư viện native đã được kiểm thử kỹ lưỡng.

PDFium là engine kết xuất cốt lõi được sử dụng bởi Google Chrome. Vì Chrome xử lý hàng triệu PDF không đáng tin cậy mỗi ngày, PDFium phải chịu sự kiểm thử fuzzing liên tục, mạnh mẽ từ Project Zero của Google. Nó xử lý các XRef sai định dạng, các luồng bị hỏng và các tham chiếu vòng một cách mượt mà.

Tương tự, các component native như HotPDF ComponentDelphi PDF Library tích hợp sẵn các chiến lược phân tích cú pháp phòng thủ mạnh mẽ. Chúng triển khai kiểm tra giới hạn nghiêm ngặt, bộ giới hạn độ sâu đệ quy và các cơ chế ngăn chặn rò rỉ bộ nhớ được thiết kế riêng cho các môi trường Delphi và C++Builder.

Cho dù bạn chọn tiêu thụ PDFium thông qua một wrapper Delphi để kết xuất, hay sử dụng các component native như HotPDF để tạo và xử lý tài liệu, bạn đều được kế thừa một vành đai bảo mật cấp doanh nghiệp, bảo vệ người dùng và máy chủ của bạn khỏi các payload độc hại mà không cần phải tự viết các trình phân tích cú pháp phòng thủ.

Lưu ý: Các khả năng phân tích cú pháp an toàn, đã qua kiểm thử fuzzing có sẵn trên toàn bộ bộ sản phẩm của chúng tôi, bao gồm HotPDF Component, Delphi PDF Library, và PDFium Component.