💡 Ví dụ hoạt động đầy đủ có sẵn trên GitHub:
load-untrusted-documents-safely-python

Cách Cũ Đầy Đau Đớn

Bạn đã viết ba dòng để tạo hình thu nhỏ của tài liệu đã tải lên. Chúng trông như sau và trông ổn:

with signature.Signature(upload_path) as sign:
    save_page_preview(sign, thumbnail_path)

Những dòng này, trước GroupDocs.Signature 26.9, đã tải về mọi địa chỉ mà tài liệu trỏ tới. Một file Word có thể chứa một hình ảnh mà nó không lưu trữ - file lưu một URL, và bất kỳ phần mềm nào mở nó sẽ tải URL đó. Trên máy tính để bàn đây là một tính năng. Trên máy chủ nhận tải lên, điều này có nghĩa là người gửi file quyết định địa chỉ nào hạ tầng của bạn sẽ yêu cầu.

Cuộc tấn công này có tên là server-side request forgery (SSRF), và có ba hình thái đáng kể. Một địa chỉ nội bộ không thể truy cập từ internet nhưng có thể truy cập từ máy chủ của bạn, vì vậy một tài liệu được tạo ra có thể khiến dịch vụ của bạn tải http://169.254.169.254/ hoặc một endpoint quản trị trên localhost. Một đường dẫn UNC có thể khiến máy Windows xác thực ra ngoài, cung cấp thông tin đăng nhập cho máy chủ do kẻ tấn công kiểm soát. Và một liên kết tới một máy chủ không bao giờ trả lời sẽ giữ luồng tải cho đến khi hết thời gian, đây là cách rẻ để làm cạn kiệt pool worker với các tài liệu trông vô hại.

Không có gì ở đây là lỗi trong thư viện tài liệu. Theo một liên kết là những gì định dạng yêu cầu. Phần khó chịu là việc tuân theo mặc định, trong mã không ai sẽ đánh dấu trong quá trình xem xét.

Có Cách Tốt Hơn

Việc tải tài liệu an toàn là hành vi của GroupDocs.Signature cho Python mà từ chối thực hiện các yêu cầu đó. Từ phiên bản 26.9, LoadOptions.skip_external_resources mặc định là True, vì vậy ba dòng giống như trước giờ không tải gì và hiển thị một hình giữ chỗ nơi hình ảnh được liên kết sẽ xuất hiện.

Thay đổi này là một giá trị mặc định chứ không phải tính năng mới - thuộc tính này đã tồn tại. Điều 26.9 thay đổi là cách nó hoạt động khi mã của bạn không chỉ định gì, đây là thiết lập duy nhất mà hầu hết dịch vụ sử dụng.

Cách Mới: Ba Chế Độ Tải

Bước 1 - Giữ mặc định cho mọi thứ không đáng tin cậy

Không có LoadOptions nào cả:

with signature.Signature(source_path) as sign:
    return save_page_preview(sign, preview_path)

Không có yêu cầu nào được thực hiện. Bản xem trước nhỏ hơn so với khi có yêu cầu, và sự khác biệt về kích thước này là bằng chứng thuận tiện nhất cho thấy không có yêu cầu nào ra khỏi máy.

Bước 2 - Cho phép danh sách trắng một máy chủ mà bạn thực sự sở hữu

Nhiều tài liệu liên kết tới một nơi hợp pháp: CDN của công ty, máy chủ hình ảnh nội bộ, kho mẫu. Cho phép điều đó và không cho phép gì khác:

load_options = LoadOptions()
load_options.whitelisted_resources = [trusted_address]

with signature.Signature(source_path, load_options) as sign:
    return save_page_preview(sign, preview_path)

Quy tắc khớp cần được chú ý. Nó là kiểm tra chuỗi con không phân biệt chữ hoa chữ thường đối với địa chỉ tài nguyên, điều này làm cho một đoạn ngắn trở nên nguy hiểm: github khớp với github.attacker.example/payload.png cũng dễ dàng như máy chủ bạn muốn. Hãy sử dụng scheme, host và path - ví dụ này cho phép danh sách trắng raw.githubusercontent.com/groupdocs-signature/.

Bước 3 - Cho phép mọi thứ, có chủ ý

Hành vi trước 26.9, vẫn còn khả dụng:

load_options = LoadOptions()
load_options.skip_external_resources = False

Phù hợp cho các tài liệu do ứng dụng của bạn tạo ra. Một bẫy: thuộc tính lỗi thời load_external_resources có hướng ngược lại, vì vậy skip_external_resources = False thay thế cho load_external_resources = True. Sao chép giá trị từ thuộc tính cũ và bạn sẽ đảo ngược vị thế bảo mật mà không có lỗi nào cảnh báo.

So Sánh: Trước và Sau

Cùng một tài liệu, cùng một luồng mã, ba chính sách tải. Đây là kích thước của các file trong thư mục Result/ của mẫu, vì vậy chúng có thể được kiểm tra thay vì chỉ tin tưởng:

Chế độ tải Kích thước xem trước Yêu cầu ra ngoài
mặc định (26.9 và sau) 16,435 bytes none
máy chủ trong danh sách trắng 51,738 bytes one, to the allowed address
tất cả tài nguyên (mặc định trước 26.9) 51,738 bytes one per linked resource

Hình ảnh được liên kết chiếm 35,303 bytes của sự khác biệt đó. Tôi đã không tin vào cài đặt cho đến khi hai con số này được đặt cạnh nhau, và tôi sẽ đề xuất cùng cách: đọc lại thuộc tính cho biết bạn đã cấu hình gì, không phải những gì quá trình đã thực hiện.

Những Gì Được Xem Là Tài Nguyên Bên Ngoài?

Hẹp hơn so với mong đợi của mọi người, vì vậy việc nâng cấp thường không gây bất ngờ. Hình ảnh được liên kết thay vì nhúng, các trường INCLUDEPICTURE, hình ảnh liên kết trong bản trình bày và bảng tính, và các hình ảnh và stylesheet mà SVG tham chiếu. Nội dung nhúng không bị ảnh hưởng, vì nó đã nằm trong file và không cần yêu cầu nào để hiển thị.

Sự khác biệt đó là ranh giới bảo mật toàn bộ. Một tài liệu chỉ có thể khiến máy chủ của bạn truy cập ra ngoài nếu nó lưu một địa chỉ thay vì các byte, vì vậy câu hỏi cho bất kỳ tập hợp nào là có bao nhiêu file của nó liên kết thay vì nhúng. Nếu không có file nào, mặc định mới không tốn gì và bạn có thể nâng cấp mà không cần đọc tiếp.

Ví Dụ Thực Tế: Tải Lên Được Ký

Trường hợp mà thay đổi mặc định tồn tại. Một tài liệu đến từ bên ngoài, và bạn cần đặt chữ ký lên nó:

with signature.Signature(source_path) as sign:
    options = QrCodeSignOptions("Approved by GroupDocs.Signature")
    options.encode_type = QrCodeTypes.QR
    options.left = 400
    options.top = 50
    options.width = 120
    options.height = 120

    result = sign.sign(output_path, options)

Không có tài nguyên bên ngoài nào được yêu cầu trong khi tài liệu được tải, ký hoặc lưu. Đầu ra đã ký vẫn giữ liên kết, vì vậy người dùng mở nó trong Word sau này vẫn thấy hình ảnh được giải quyết trên máy của họ. Bỏ qua là một chính sách phía máy chủ, không phải chỉnh sửa tài liệu - điều này chính là lý do khiến nó an toàn khi áp dụng cho các file bạn xử lý thay mặt người khác.

Những Điều Khác Thay Đổi Khi Bạn Nâng Cấp?

Đối với hầu hết các dịch vụ, không có gì đáng chú ý, điều này đáng nói rõ vì một mặc định bảo mật thay đổi hành vi ở mọi nơi sẽ không vượt qua được việc xem xét nâng cấp. Việc ký, xác minh và tìm kiếm không bị ảnh hưởng. Ngoại lệ duy nhất là bản xem trước trước đây hiển thị hình ảnh liên kết và bây giờ hiển thị hình giữ chỗ - thay đổi đang thực hiện công việc của nó. Cho phép danh sách trắng máy chủ nếu đó là của bạn, chấp nhận nếu không.

Cần nhấn mạnh riêng: SVG. Một SVG có thể tham chiếu hình ảnh và stylesheet bằng URL, các tham chiếu này là tài nguyên bên ngoài theo cùng quy tắc, và SVG vừa là định dạng tải lên phổ biến vừa là vector SSRF thường gặp. Một dịch vụ chấp nhận avatar SVG và render chúng phía máy chủ chính là hệ thống mà thay đổi này bảo vệ.

Một Chi Tiết Python: Cách Xem Trước Được Ghi

PreviewOptions nhận hai hàm tạo stream thay vì một đường dẫn, và các callable Python đơn giản là tất cả những gì nó cần:

def create_page_stream(page_data):
    return open(preview_path, "wb")

def release_page_stream(page_data, page_stream):
    page_stream.close()

preview_options = PreviewOptions(create_page_stream, release_page_stream)
preview_options.preview_format = PreviewOptions.PreviewFormats.PNG
sign.generate_preview(preview_options)

Một hàm tạo stream cho mỗi trang, hàm còn lại giải phóng nó. Tài liệu mẫu có một trang duy nhất, vì vậy một file được ghi; đối với đầu vào đa trang, bao gồm số trang trong tên hoặc mỗi trang sẽ ghi đè lên trang trước.

Kết Luận

Mặc định đã được đảo ngược để hành vi rủi ro cần quyết định rõ ràng và hành vi an toàn không cần gì. Giữ mặc định cho đầu vào không đáng tin, cho phép danh sách trắng một cách hẹp khi máy chủ của bạn liên quan, và nhớ rằng việc ký không bao giờ cần mạng.

Nếu bạn muốn kiểm tra mạnh hơn chỉ dựa vào kích thước file, hãy trỏ một tài liệu thử nghiệm tới một máy chủ bạn kiểm soát và xem log truy cập trong khi bản xem trước chạy. Kích thước cho bạn biết byte đã đến chưa; log truy cập cho bạn biết có yêu cầu nào được thực hiện hay không, và chúng khác nhau đúng trong trường hợp quan trọng - một máy chủ trong danh sách trắng nhưng không thể truy cập sẽ trông giống như một máy chủ bị chặn chỉ dựa trên đầu ra.

Chạy mẫu với một trong các tài liệu của bạn mất một phút và cho bạn biết, qua ba kích thước file, chính xác những gì dịch vụ của bạn đã tải về thay mặt người gửi file.

Tài Nguyên Bổ Sung