Bỏ qua để đến nội dung

NextPDF Gotenberg: bảo mật và vận hành

Bridge này gửi tài liệu từ ứng dụng của bạn đến một dịch vụ bên ngoài qua mạng. Vì vậy, nó vừa là bề mặt yêu cầu phía máy chủ vừa là bề mặt bảo mật truyền tải. Gói này triển khai các biện pháp kiểm soát cụ thể, có thể kiểm chứng cho cả hai lớp đó. Tự thân nó không bảo mật toàn bộ hệ thống. Những biện pháp kiểm soát này chỉ hiệu quả khi bạn triển khai và vận hành Gotenberg với các lớp bảo vệ tương ứng. Trang này giải thích các biện pháp kiểm soát mà gói triển khai, cũng như các trách nhiệm vận hành cần có để hoàn thiện chúng.

Không nội dung nào ở đây là một sự bảo đảm. Mỗi biện pháp kiểm soát là một hành vi được định nghĩa rõ, có kiểm thử bao phủ và có giới hạn được nêu rõ.

Bridge áp dụng hai chính sách bảo mật riêng biệt ở hai lớp khác nhau:

  • Chính sách truyền tải (GotenbergSecurityPolicy) — bắt buộc lược đồ URL, sàng lọc giả mạo yêu cầu phía máy chủ (SSRF), phòng vệ trước tấn công rebinding Domain Name System (DNS), giới hạn kích thước đầu vào và sàng lọc tên tệp. Đây là lớp được trình bày chi tiết bên dưới.
  • Chính sách phân tích cú pháp HTML — một chính sách nội dung ở lớp phân tích cú pháp, mặc định là chính sách mặc định của lõi NextPDF, chạy trước khi nội dung đến bộ kết xuất. Chính sách này bổ sung cho chính sách truyền tải nhưng vẫn độc lập với chính sách đó. Trang này tập trung vào chính sách truyền tải.

Sàng lọc giả mạo yêu cầu phía máy chủ (SSRF)

Phần tiêu đề “Sàng lọc giả mạo yêu cầu phía máy chủ (SSRF)”

Bridge sàng lọc URL API đã cấu hình trước khi bất kỳ byte nào rời khỏi tiến trình. Biện pháp kiểm soát này gồm ba phần.

Bắt buộc lược đồ. Chỉ https được chấp nhận (không phân biệt chữ hoa, chữ thường). Một URL http:// thuần túy sẽ bị từ chối. Do đó, Transport Layer Security (TLS) là bắt buộc cho mọi lần chuyển đổi và cho thăm dò tình trạng (health probe).

Sàng lọc địa chỉ. Nếu host là một địa chỉ IP trực tiếp (IP literal), bridge sẽ từ chối khi địa chỉ đó nằm trong dải riêng tư hoặc dải dành riêng. Nếu host là một tên, bridge phân giải tên đó thành tất cả các bản ghi A và AAAA, rồi từ chối yêu cầu nếu bất kỳ địa chỉ đã phân giải nào là riêng tư hoặc dành riêng. Việc phân giải toàn bộ tập bản ghi, thay vì chỉ một địa chỉ đơn lẻ, là biện pháp kiểm soát dùng để chặn kẻ tấn công giấu một địa chỉ riêng tư phía sau một tên miền cũng trả về địa chỉ công khai. Cách làm này tuân theo hướng dẫn phòng chống SSRF của OWASP: lấy mọi địa chỉ IP nằm sau tên miền (các bản ghi A và AAAA, cho IPv4 và IPv6), rồi xác thực từng địa chỉ với danh sách cho phép (OWASP Cheat Sheet Series, phòng chống SSRF, phòng vệ ở lớp ứng dụng; được ghim trong sidecar retrieval-augmented generation (RAG) của trang).

Kiểm tra lại theo thời điểm kiểm tra/thời điểm sử dụng (TOCTOU). Bridge phân giải lại và so sánh tập địa chỉ đã ghi nhận trong quá trình xác thực ngay trước khi gửi yêu cầu. Nếu xuất hiện một địa chỉ mới, yêu cầu bị hủy với lỗi rebinding DNS. Điều này đóng lại khoảng thời gian giữa xác thực và kết nối, vốn là điểm mà một cuộc tấn công rebinding có thể khai thác.

Khi bridge dùng lớp truyền tải ghim bằng cURL, tập địa chỉ đã xác thực được gắn vào kết nối thông qua CURLOPT_RESOLVE, để nhân (kernel) kết nối đến địa chỉ đã được thẩm định thay vì bất kỳ địa chỉ nào mà một lần tra cứu DNS mới tại thời điểm kết nối có thể trả về. Việc đi theo chuyển hướng bị tắt trên lớp truyền tải đó (CURLOPT_FOLLOWLOCATION tắt, CURLOPT_MAXREDIRS bằng không), nên một phản hồi 3xx không thể âm thầm gửi yêu cầu đến một host chưa được thẩm định. Thay vào đó, lớp chính sách sẽ nhận phản hồi.

Hệ quả vận hành. Theo thiết kế, bộ bảo vệ SSRF từ chối các địa chỉ riêng tư và dành riêng. Nếu Gotenberg của bạn chạy trên mạng riêng, bạn không thể trỏ bridge đến địa chỉ riêng của nó. Hãy cung cấp dịch vụ đó qua một địa chỉ mà bộ bảo vệ chấp nhận, rồi bảo vệ đường dẫn này bằng phân đoạn mạng và xác thực như mô tả trong phần triển khai bên dưới.

Bảo mật truyền tải và ghim khóa công khai

Phần tiêu đề “Bảo mật truyền tải và ghim khóa công khai”

Xác minh peer và host của TLS luôn được bật trong lớp truyền tải ghim bằng cURL (CURLOPT_SSL_VERIFYPEER bằng true, CURLOPT_SSL_VERIFYHOST bằng 2). Ngoài xác thực chuỗi chứng chỉ tiêu chuẩn, bridge còn hỗ trợ ghim SubjectPublicKeyInfo (SPKI).

Mỗi pin là một hash SHA-256 của SubjectPublicKeyInfo của máy chủ, được biểu diễn dưới dạng sha256/<base64>. Bridge chấp nhận một chứng chỉ khi hash SPKI của chứng chỉ đó khớp với bất kỳ pin nào trong tập kết hợp gồm pin chính và pin dự phòng. Mô hình pin dự phòng này tuân theo RFC 7469 §4.3, vốn xác định pin dự phòng — dấu vân tay của một cặp khóa thứ cấp, chưa được triển khai — là đường khôi phục chính cho lỗi xác thực pin ngoài ý muốn, và §2.5, vốn yêu cầu tập pin phải bao gồm ít nhất một pin không có trong chuỗi chứng chỉ hiện tại (RFC 7469 §4.3 và §2.5; được ghim trong sidecar RAG của trang). Mã của bridge khai báo RFC 7469 §2.1 và §2.6 cho ngữ nghĩa của quy tắc ít nhất một pin dự phòng và phép giao của tập kết hợp. Ghim là tính năng bật tùy chọn: khi không cấu hình pin nào, xác thực chuỗi chứng chỉ tiêu chuẩn được áp dụng và ghim không được thực thi.

Một pin không phân tích được sẽ gây lỗi cấu hình trước khi có bất kỳ yêu cầu nào. Một chứng chỉ đang hoạt động có SPKI không khớp với pin nào đã cấu hình sẽ khiến lớp truyền tải làm yêu cầu thất bại — đây là hành vi theo thiết kế.

Một lần xoay sai sẽ khóa bridge khỏi dịch vụ. Để xoay mà không gây gián đoạn:

  1. Trước khi thay đổi khóa máy chủ, hãy tạo pin SPKI cho khóa mới và thêm nó vào danh sách pin dự phòng. Triển khai cấu hình đó. Bridge giờ chấp nhận cả khóa hiện tại lẫn khóa tương lai.
  2. Chuyển đổi chứng chỉ hoặc khóa máy chủ để dùng khóa mới.
  3. Xác nhận các lần chuyển đổi vẫn thành công (khóa mới giờ khớp với pin dự phòng).
  4. Chuyển pin mới từ danh sách dự phòng sang danh sách chính và xóa pin của khóa đã ngừng sử dụng. Triển khai.
  5. Tạo và chuẩn bị sẵn pin cho lần xoay tiếp theo làm pin dự phòng mới để tập pin luôn có một pin thay thế dùng được.

Giữ danh sách dự phòng tách biệt với danh sách chính giúp bạn chuẩn bị và xác thực pin tiếp theo mà không động đến pin đang dùng.

Khi apiKey không rỗng, bridge gửi giá trị đó dưới dạng một header Authorization: Bearer trên yêu cầu chuyển đổi. Trường này được đánh dấu #[\SensitiveParameter] để giá trị được che khỏi các vết ngăn xếp (stack trace). Bridge không tải bí mật thay bạn; hãy cung cấp bí mật từ một trình quản lý bí mật khi tiến trình khởi động và không bao giờ commit nó. Token không được ghi vào nhật ký yêu cầu — mục debug được ghi nhật ký chỉ chứa URL, tên tệp, định dạng và độ dài nội dung.

Một phản hồi chỉ được chấp nhận khi trạng thái là 200, Content-Type chứa application/pdf, và phần thân bắt đầu bằng chữ ký %PDF. Việc kiểm tra chữ ký byte rất quan trọng vì chỉ riêng content type được khai báo không chứng minh được các byte thực sự là gì. Tiêu chuẩn WHATWG MIME Sniffing chính thức hóa cùng lập luận đó trong thuật toán dò loại MIME của nó, vốn suy ra một loại được tính toán từ việc khớp mẫu byte ở đầu thay vì từ loại được cung cấp. Hướng dẫn tải tệp lên của OWASP nêu nguyên tắc ứng dụng tương ứng: hãy xác thực loại tệp và không tin vào header Content-Type được khai báo, vì nó có thể bị giả mạo (WHATWG MIME Sniffing §6.2.3; OWASP Cheat Sheet Series, xác thực tải tệp lên; cả hai đều được ghim trong sidecar RAG của trang). Bridge áp dụng phép kiểm tra tương đương theo hướng phòng vệ ở phía dữ liệu đến: một sự không khớp sẽ gây ra một ngoại lệ có kiểu, và các byte sẽ không bao giờ được trả về như một kết quả.

Ranh giới này cũng là lý do hợp đồng PSR-18 quan trọng ở đây. Một client PSR-18 chỉ ném ngoại lệ khi nó không thể gửi yêu cầu hoặc không thể phân tích phản hồi thành một đối tượng PSR-7 — nó không ném ngoại lệ chỉ vì mã trạng thái là lỗi. Một phản hồi 4xx/5xx đúng định dạng được trả về cho bên gọi như bình thường (PSR-18, “Exceptions”; được ghim trong sidecar RAG của trang). Vì vậy, bridge tự kiểm tra trạng thái, loại và chữ ký thay vì giả định một phản hồi được trả về là thành công. Ngữ nghĩa HTTP cho vi phạm ràng buộc content-type — từ chối 415 (Unsupported Media Type), khi máy chủ từ chối nội dung ở một định dạng không được hỗ trợ — chính là mô hình mà việc kiểm tra dữ liệu đến phản chiếu theo (RFC 9110 §15.5.16; được ghim trong sidecar RAG của trang).

Bridge có một giới hạn tài nguyên trong tiến trình: maxFileSize (mặc định 52.428.800 byte = 50 MiB). Giới hạn này được thực thi trước khi gửi yêu cầu, nên đầu vào quá khổ sẽ không bao giờ đến được dịch vụ. Bridge không có giới hạn đồng thời, giới hạn tốc độ hay giới hạn kích thước đầu ra tích hợp sẵn. Đó là trách nhiệm của phần triển khai và của bên gọi (xem /integrations/gotenberg/production-usage/). Hãy đặt maxFileSize ở giá trị nhỏ nhất mà tài liệu thực tế của bạn cần — một giới hạn chặt hơn là biện pháp kiểm soát từ chối dịch vụ rẻ hơn.

Triển khai và bảo mật dịch vụ Gotenberg

Phần tiêu đề “Triển khai và bảo mật dịch vụ Gotenberg”

Bridge chỉ an toàn ngang với dịch vụ mà nó gọi. Bạn là người vận hành dịch vụ đó; các trách nhiệm bên dưới hoàn thiện những biện pháp kiểm soát ở trên.

  • Kết thúc TLS ở phía trước Gotenberg. Mặc định, container của Gotenberg dùng HTTP thuần túy. Bridge yêu cầu HTTPS, vì vậy hãy đặt Gotenberg sau một reverse proxy, ingress hoặc service mesh có kết thúc TLS và trỏ bridge đến endpoint HTTPS. Hãy ghim SPKI của proxy nếu bạn bật ghim.
  • Không phơi bày Gotenberg công khai. Nó thực hiện chuyển đổi tài liệu bằng các engine thuộc lớp LibreOffice và Chromium, và không phải là dịch vụ hướng ra internet. Hãy dùng chính sách mạng hoặc quy tắc tường lửa để giới hạn lưu lượng vào chỉ cho các host ứng dụng gọi nó.
  • Yêu cầu xác thực trên đường dẫn. Bridge gửi một bearer token khi được cấu hình; hãy thực thi nó (hoặc TLS hai chiều) tại proxy để một yêu cầu chưa xác thực không thể đến được engine chuyển đổi.
  • Ghim một phiên bản dịch vụ cụ thể. Bridge giả định chính xác hai đường dẫn dịch vụ — /forms/libreoffice/convert/health. Hãy ghim image Gotenberg vào một patch tag cụ thể, kiểm tra hai đường dẫn đó với phiên bản bạn triển khai, và kiểm tra lại ở mỗi lần nâng cấp.
  • Định cỡ năng lực chuyển đổi có chủ đích. Mỗi lần chuyển đổi giữ một worker trong suốt thời gian của yêu cầu. Hãy định cỡ bản triển khai Gotenberg theo tỷ lệ chuyển đổi đồng thời ở mức đỉnh của bạn và giới hạn số lần chuyển đổi đang xử lý ở phía bên gọi cho tương xứng. Năng lực là thuộc tính của bản triển khai của bạn, không phải của gói này.
  • Xem các đầu vào chuyển đổi là không đáng tin. Các tài liệu được đẩy qua quá trình chuyển đổi được xử lý bởi các engine phức tạp. Hãy ràng buộc maxFileSize, cô lập bản triển khai Gotenberg (phân đoạn mạng riêng của nó, lưu lượng ra tối thiểu, không có quyền truy cập các dịch vụ nội bộ), và giữ engine luôn được vá lỗi.
  • Nó không “an toàn theo mặc định”: các biện pháp kiểm soát là có thật, nhưng chúng phụ thuộc vào việc triển khai và cấu hình đúng.
  • Nó không làm cho việc chuyển đổi trở nên “chống giả mạo” hay đầu ra trở thành “được chứng nhận”. Nó xác thực lớp truyền tải và hình dạng của phản hồi; nó không chứng thực nội dung tài liệu.
  • Nó không cung cấp việc ký, đóng dấu thời gian (timestamping) hay xác thực dài hạn. Đó là các vấn đề thuộc khâu xử lý hậu kỳ. Hỗ trợ PAdES của phiên bản Pro chỉ là cơ sở B-B và không bao gồm B-T, B-LT hay B-LTA; không có gì trong bridge này ngụ ý một khả năng đóng dấu thời gian hoặc LTV.
  • Nó không hỗ trợ “tất cả các tệp Office”. Nó hỗ trợ sáu định dạng được liệt kê và từ chối mọi thứ khác trước bất kỳ yêu cầu nào.
  • /integrations/gotenberg/configuration/ — các quy tắc chọn lớp truyền tải và mô hình pin đầy đủ.
  • /integrations/gotenberg/production-usage/ — thử lại, hết thời gian chờ (timeout), xử lý đồng thời và khả năng quan sát.
  • /integrations/gotenberg/troubleshooting/ — mọi ngoại lệ bảo mật và nguyên nhân kích hoạt tương ứng.
  • /integrations/gotenberg/overview/ — luồng chuyển đổi và mô hình phụ thuộc.