💡 Ví dụ hoạt động đầy đủ có trên GitHub:
bỏ-tài-nguyên-bên-nhà-khi-ký-dotnet

Giới thiệu

Một tài liệu Word có thể chứa một hình ảnh không nằm trong tệp. Tài liệu giữ một địa chỉ, và bất kỳ phần mềm nào mở nó sẽ truy xuất địa chỉ đó. Trên máy tính để bàn đây là một tính năng – hình ảnh sẽ cập nhật khi nguồn thay đổi. Trên máy chủ nhận tải lên, điều này có nghĩa là người gửi tệp quyết định URL nào hạ tầng của bạn sẽ yêu cầu.

Việc tải tài liệu an toàn là một hành vi của GroupDocs.Signature cho .NET, từ chối thực hiện các yêu cầu đó. Từ phiên bản 26.9, LoadOptions.SkipExternalResources mặc định là true. Bài viết này so sánh ba chế độ tải đối với cùng một tài liệu, chỉ ra cách cho phép một máy chủ mà không cho phép tất cả, và giải thích tại sao ký một tệp không tin cậy không cần truy cập mạng.

Tại sao vấn đề này quan trọng hơn so với vẻ ngoài

Cuộc tấn công có tên – server‑side request forgery (SSRF) – và ba hình thái cụ thể.

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 truy xuất http://169.254.169.254/ hoặc một endpoint quản trị trên localhost và, tùy thuộc vào cách bạn xử lý kết quả, rò rỉ thông tin. Một đường UNC trong tài liệu có thể khiến một 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ẽ làm luồng tải bị kẹt cho đến khi hết thời gian, là cách rẻ tiền để làm cạn kiệt pool worker.

Tôi đã cho rằng đây chỉ là một mối quan ngại lý thuyết cho đến khi thấy một tài liệu thử nghiệm kéo một hình ảnh qua một dịch vụ không có nhiệm vụ thực hiện các yêu cầu ra ngoài. Không có gì trong số này yêu cầu một lỗi trong thư viện tài liệu. Việc theo liên kết là những gì định dạng yêu cầu; câu hỏi duy nhất là máy chủ của bạn có nên đáp ứng hay không.

Phương pháp 1 – Mặc định mới

Không có LoadOptions nào:

using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);

Không có gì được tải về. Bản xem trước hiển thị một chỗ trống nơi hình ảnh liên kết sẽ xuất hiện, và file PNG nhỏ hơn so với khi không có chế độ này. Sự khác biệt về kích thước là bằng chứng thuận tiện nhất cho thấy không có yêu cầu nào rời khỏi máy.

Các tính năng nào được tính là bên ngoài? Hình ảnh liên kết thay vì nhúng, trường INCLUDEPICTURE, hình ảnh liên kết trong bản trình chiếu và bảng tính, và các hình ảnh và stylesheet mà một SVG tham chiếu. Nội dung nhúng không bị ảnh hưởng – nó đã có trong tệp.

Phương pháp 2 – Danh sách trắng một địa chỉ

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

var loadOptions = new LoadOptions
{
    WhitelistedResources = new List<string> { trustedAddress }
};

using var signature = new Signature(sourcePath, loadOptions);

Quy tắc khớp cần được chú ý. Nó là một 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, nghĩa là một đoạn ngắn có thể nguy hiểm: github khớp với github.attacker.example/payload.png cũng như máy chủ bạn muốn. Hãy sử dụng scheme, host và path – mẫu whitelist raw.githubusercontent.com/groupdocs-signature/.

Phương pháp 3 – Cho phép mọi thứ

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

var loadOptions = new LoadOptions { SkipExternalResources = false };

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

So sánh ba chế độ: Khi nào dùng mỗi chế độ

Chế độ Tốt nhất cho Ưu điểm chính Hạn chế
Mặc định (bỏ qua) tải lên của người dùng, email, tệp đối tác không có yêu cầu outbound nào có thể xảy ra hình ảnh liên kết hiển thị dưới dạng chỗ trống
Danh sách trắng tài liệu liên kết tới máy chủ của bạn giữ các liên kết hợp pháp hoạt động khớp chuỗi con cần một đoạn dài, cụ thể
Cho phép tất cả tệp do hệ thống của bạn tạo bản xem trước hiển thị chính xác như trước khôi phục lại rủi ro SSRF mà mặc định đã loại bỏ

Việc ký có cần các tài nguyên này không?

Không, và đây là lợi ích thực tế. Chữ ký QR‑code được áp dụng với cài đặt tải mặc định và không có tài nguyên bên ngoài nào được yêu cầu khi tài liệu được tải, ký hoặc lưu:

var options = new QrCodeSignOptions("Approved by GroupDocs.Signature")
{
    EncodeType = QrCodeTypes.QR,
    Left = 400,
    Top = 50,
    Width = 120,
    Height = 120
};

SignResult result = signature.Sign(outputPath, options);

Kết quả đã ký vẫn giữ liên kết, vì vậy người dùng mở tài liệu sau này vẫn sẽ 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 là một chỉnh sửa tài liệu – điều này làm cho nó an toàn khi áp dụng cho các tệp bạn xử lý thay mặt người khác.

Những gì 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ì hiển thị ngay lập tức, và điều này đáng được nêu rõ vì một mặc định bảo mật thay đổi hành vi ở mọi nơi sẽ không qua được kiểm tra nâng cấp. Ngoại lệ là bất kỳ nơi nào bản xem trước hoặc thumbnail trước đây hiển thị hình ảnh liên kết và giờ hiển thị chỗ trống; đó là thay đổi đang thực hiện công việc của nó, và cách khắc phục là thêm mục whitelist nếu máy chủ là của bạn, hoặc chấp nhận nếu tài liệu đến từ bên ngoài.

Cách kiểm tra trung thực là cách mẫu sử dụng: render cùng một tài liệu dưới ba chế độ và so sánh kích thước đầu ra. Nếu kích thước bản xem trước mặc định và bản whitelist giống nhau, nghĩa là không có gì được tải về trong cả hai trường hợp – thường là vì máy chủ không thể tiếp cận từ máy đó hơn là whitelist thất bại, và mẫu sẽ in ra gợi ý nói chính xác như vậy.

Trợ giúp Preview, Vì Nó Không Rõ Ràng

Hai trong ba chế độ trên gọi một trợ giúp nhỏ, và đáng để hiển thị vì PreviewOptions không nhận đường dẫn:

var previewOptions = new PreviewOptions(
    pageData => File.Create(previewPath),
    (pageData, pageStream) => pageStream.Dispose())
{
    PreviewFormat = PreviewOptions.PreviewFormats.PNG
};

signature.GeneratePreview(previewOptions);

Nó nhận hai factory stream – một để tạo stream cho mỗi trang, một để giải phóng. Tài liệu mẫu có một trang duy nhất, vì vậy chỉ một file được ghi; với đầu vào đa trang, hãy đặt số trang vào tên file hoặc mỗi trang sẽ ghi đè lên trang trước.

Thực hành tốt

  • Xem mọi thứ bạn không tự tạo là không tin cậy, bao gồm cả tệp từ đối tác có chính sách bảo mật tốt.
  • Đảm bảo các đoạn whitelist đủ dài để không gây nhầm lẫn, và xem xét lại chúng khi CDN thay đổi.
  • Không bao giờ đặt SkipExternalResources dựa trên giá trị trước đây được gán cho LoadExternalResources.
  • Xác minh bằng kích thước đầu ra thay vì chỉ dựa vào cài đặt; một cấu hình trông đúng và một yêu cầu không xảy ra là hai khẳng định khác nhau.

Nơi này để SVG

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

Nếu pipeline của bạn chấp nhận SVG từ người dùng, mặc định là cài đặt bạn muốn, và whitelist chỉ dùng khi mẫu của bạn tự lấy stylesheet chung từ một máy chủ bạn quản lý.

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ì cả. Giữ mặc định cho đầu vào không tin cậy, whitelist hẹp khi máy chủ của bạn tham gia, và nhớ rằng việc ký bản thân không bao giờ cần mạng. Chạy mẫu với một 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ề.

Tài nguyên bổ sung