Cập nhật gia tăng và vì sao chúng quan trọng
ISO 32000-2 §7.5.6 Evidence: Standard-backed
Tổng quan nhanh
Phần tiêu đề “Tổng quan nhanh”Khi một tệp PDF thay đổi sau khi đã được ghi, cách lưu an toàn không phải là ghi lại toàn bộ tệp. Thay vào đó, bạn nối thêm các đối tượng đã thay đổi và một mục cross-reference mới vào cuối tệp, giữ nguyên mọi byte gốc ở đúng vị trí ban đầu. Trang này giải thích cơ chế đó hoạt động ra sao và vì sao nhờ vậy một chữ ký số có thể tồn tại qua một lần chỉnh sửa sau đó.
Vì sao điều này quan trọng
Phần tiêu đề “Vì sao điều này quan trọng”Chữ ký bảo vệ một dải byte. Nếu việc lưu một thay đổi chỉ sửa một từ mà lại ghi lại toàn bộ tệp, thì mọi byte offset đều sẽ dịch chuyển. Dải đã ký sẽ không còn khớp với nội dung ban đầu nữa. Chữ ký sẽ hỏng, ngay cả khi bản thân nội dung đã ký không hề bị động đến.
Cập nhật gia tăng tồn tại để điều đó không xảy ra. Các byte gốc, bao gồm cả những byte mà một chữ ký bao phủ, vẫn giữ nguyên vị trí. Người kiểm tra có thể lấy một tài liệu đã ký rồi mới chỉnh sửa và xác minh chữ ký đầu tiên dựa trên bản sửa đổi gốc. Họ thấy chính xác những gì đã được ký và, tách biệt với phần đó, những gì đã thay đổi về sau. Làm sai điều này thì bạn hoặc sẽ vô hiệu hóa các chữ ký hợp lệ, hoặc tệ hơn, mất khả năng chứng minh chữ ký thực sự đã xác nhận điều gì.
Tóm tắt ngắn gọn
Phần tiêu đề “Tóm tắt ngắn gọn”- Một bản cập nhật gia tăng nối thêm: các đối tượng mới và đã thay đổi, rồi một mục cross-reference mới, rồi một trailer mới, tất cả đều ở cuối tệp.
- Nội dung gốc của tệp được giữ nguyên vẹn — không bị chỉnh sửa tại chỗ.
- Trailer mới mang một mục
/Prev: byte offset của mục cross-reference trước đó. Các mục này tạo thành một chuỗi liên kết ngược. - Một trình đọc xây dựng chỉ mục bằng cách đi theo chuỗi đó, bắt đầu từ mục mới nhất. Với bất kỳ số đối tượng nào, mục mới nhất sẽ thắng.
- Vì không có gì bị ghi đè, dải byte mà một chữ ký trước đó bao phủ vẫn giống hệt từng byte một — nên chữ ký vẫn xác minh được, và bạn có thể khôi phục tài liệu đúng như khi nó được ký.
NextPDF tiếp cận điều này như thế nào
Phần tiêu đề “NextPDF tiếp cận điều này như thế nào”NextPDF ghi tài liệu nền như đã mô tả ở trang trước, rồi cung cấp ba thứ mà một bản cập nhật gia tăng cần có.
Sau build(), trình ghi (src/Writer/PdfWriter.php) giữ lại:
- bộ đệm đầu ra, có thể lấy qua
getBuffer(), để một bản cập nhật có thể được nối thêm đúng vào điểm cuối của các byte hiện có; - byte offset của mục cross-reference cuối cùng, qua
getLastXrefOffset(), sẽ trở thành giá trị/Prevcủa mục mới; - các mục trong dictionary của catalog, qua
getCatalogEntries(), để một bản cập nhật buộc phải phát lại catalog (ví dụ để gắn một tham chiếu chữ ký) sẽ không làm mất bất kỳ khóa nào trước đó.
Một bản sửa đổi được nối thêm cấp phát số đối tượng mới (hoặc tái sử dụng số hiện có cho các đối tượng mà nó thay thế) dựa trên cùng một ObjectRegistry, nhờ vậy việc đánh số đối tượng vẫn nhất quán qua các bản sửa đổi. Mục cross-reference mới chỉ liệt kê những đối tượng mà bản sửa đổi này tác động đến. Trailer mới lặp lại các mục của trailer trước đó và thêm /Prev, trỏ ngược về mục trước đó. Chuỗi đó chính là thứ mà một trình đọc đi theo.
Điều này thể hiện rõ nhất khi ký. ByteRangeCalculator của NextPDF (src/Security/Signature/ByteRangeCalculator.php) tính mảng /ByteRange thành hai đoạn: mọi thứ trước giá trị chữ ký, và mọi thứ sau nó — nhờ vậy chữ ký bao phủ toàn bộ bản sửa đổi trừ chính các byte của nó. Vì một lần chỉnh sửa về sau được nối thêm chứ không ghi đè lên những byte đó, nên dải này không bao giờ dịch chuyển.
- Write base revision Header, body, xref section, trailer — the original bytes.
- Sign A /ByteRange digest covers the whole revision except the signature value itself.
- Edit and save Changed objects + a new xref section are appended; originals are untouched.
- New trailer chains back The appended trailer carries /Prev = offset of the previous xref section.
- Verify The first signature still covers the same unchanged bytes; the chain shows what came after.
Bằng chứng nói gì
Phần tiêu đề “Bằng chứng nói gì”Quy tắc chỉ-nối-thêm là một yêu cầu mang tính quy phạm. Spec: ISO 32000-2, §7.5.6 ISO 32000-2 §7.5.6 nêu rằng nội dung của một tệp PDF có thể được cập nhật theo cách gia tăng mà không cần ghi lại toàn bộ tệp, và khi làm vậy, các thay đổi phải được nối thêm vào cuối tệp, giữ nguyên vẹn nội dung gốc. Evidence: Standard-backed
Cùng điều khoản đó cũng định nghĩa cơ chế. Một mục cross-reference cho bản cập nhật gia tăng chỉ chứa các entry cho những đối tượng đã bị thay đổi, thay thế hoặc xóa. Các đối tượng đã xóa vẫn được giữ lại trong tệp nhưng được đánh dấu là đã xóa thông qua entry cross-reference của chúng. Trailer được thêm vào phải chứa một entry /Prev cho biết vị trí của mục cross-reference trước đó. Entry của bản cập nhật cho một đối tượng đã thay đổi mang byte offset của bản sao mới, ghi đè lên offset cũ. Một trình đọc xây dựng thông tin cross-reference sao cho bản sao mới nhất của mỗi đối tượng là bản được truy cập.
Hệ quả đối với chữ ký được nêu trực tiếp bởi
Spec: ISO 32000-2, §12.8.1 ISO 32000-2 §12.8.1 : một digest theo dải byte
được tính trên một dải của tệp — thường là toàn bộ tệp, ngoại trừ
giá trị chữ ký (entry /Contents). Tiêu chuẩn sau đó lưu ý rằng
nếu một tài liệu đã ký bị sửa đổi và lưu bằng cập nhật gia tăng, thì dữ liệu
tương ứng với dải byte của chữ ký gốc được bảo toàn, nên nếu
chữ ký hợp lệ thì trạng thái của tài liệu tại thời điểm ký có thể được
tái tạo lại. Chỉ-nối-thêm không phải là một điểm cộng tùy chọn. Nó là thuộc tính mà mô hình chữ ký
phụ thuộc vào.
Ví dụ thực tế
Phần tiêu đề “Ví dụ thực tế”Một tệp PDF đã ký rồi mới chỉnh sửa, nhìn từ góc độ cấu trúc. Bản sửa đổi gốc kết thúc tại %%EOF của riêng nó. Bản sửa đổi thứ hai được nối thêm bên dưới nó.
%PDF-2.0... original objects, including the signature dictionary ...xref0 8... entries for the original revision ...trailer<< /Size 8 /Root 1 0 R >>startxref920%%EOF <-- end of revision 1: the signed bytes stop here9 0 obj <-- revision 2, appended<< /Type /Annot /Subtype /Text /Contents (added after signing) >>endobjxref0 19 0 obj-entry...8 90000001740 00000 ntrailer<< /Size 10 /Root 1 0 R /Prev 920 >>startxref1980%%EOFMột trình xác thực đọc trailer cuối cùng, thấy /Prev 920, và từ đó có toàn bộ chuỗi. Nó có thể xác minh chữ ký dựa trên các byte tính đến %%EOF đầu tiên, vốn không thay đổi. Sau đó nó có thể báo cáo riêng rằng bản sửa đổi 2 đã thêm một annotation. Lịch sử nằm ngay trong tệp. Không có gì bị che giấu bằng cách ghi đè.
Hiểu lầm thường gặp
Phần tiêu đề “Hiểu lầm thường gặp”Điểm dễ mắc bẫy là “cập nhật gia tăng nghĩa là thay đổi nhỏ, nên nó vô hại.” Nối thêm nói về bảo toàn byte, không phải về kích thước. Một bản cập nhật gia tăng có thể thêm rất nhiều nội dung. Điều khiến nó là một bản cập nhật gia tăng là nó không động đến những byte đã có sẵn ở đó. Hệ quả đi kèm cũng dễ khiến nhiều người nhầm: một công cụ “tối ưu hóa” hoặc “tuyến tính hóa” một tệp PDF đã ký bằng cách ghi lại từ đầu sẽ tạo ra một tệp nhỏ hơn, gọn gàng hơn, kèm theo một chữ ký bị hỏng, vì dải byte đã ký không còn tồn tại nữa. Lưu gia tăng một tệp PDF đã ký và ghi lại nó từ đầu không phải là cùng một thao tác.
Giới hạn và ranh giới
Phần tiêu đề “Giới hạn và ranh giới”Chỉ-nối-thêm bảo vệ các byte. Tự thân nó không cho bạn biết liệu các thay đổi được nối thêm có được cho phép hay không. Một bản sửa đổi thứ hai có thể thêm một chữ ký thứ hai một cách hợp lệ, hoặc cũng có thể thêm nội dung mà người ký đầu tiên không hề có ý định. Quyết định đó là việc của quá trình xác thực chữ ký và chính sách phát hiện sửa đổi (DocMDP). Nối thêm là nền tảng giúp việc phân tích đó có thể thực hiện được, chứ không phải bản thân việc phân tích.
Trang này cũng không đề cập cách hai dải byte của một chữ ký được tính và ghép lại, hay những gì một quá trình xác thực đầy đủ kiểm tra. Đó là những chủ đề riêng. Bảo đảm ở đây áp dụng cho các tệp được ghi và cập nhật bởi một trình ghi tuân thủ: một tệp có các bản sửa đổi trước đó vốn đã sai định dạng thì không trở nên đúng định dạng chỉ nhờ được nối thêm vào.
Mini-FAQ
Phần tiêu đề “Mini-FAQ”Làm sao tôi biết một tệp PDF có bao nhiêu bản sửa đổi? Đếm các dấu %%EOF và đi theo chuỗi /Prev từ trailer cuối cùng. Mỗi mục cross-reference mà bạn truy theo được tương ứng với một bản sửa đổi đã lưu.
Việc xóa một đối tượng có loại bỏ nó khỏi tệp không? Không. Một bản cập nhật gia tăng đánh dấu đối tượng là đã xóa trong entry cross-reference của nó, nhưng các byte của đối tượng vẫn còn ở các bản sửa đổi trước đó. “Đã xóa” nghĩa là “không được bản sửa đổi hiện tại tham chiếu đến”, chứ không phải “đã bị xóa bỏ.”
Một bản cập nhật gia tăng có thể thay đổi phiên bản PDF không? Có, bằng cách đặt entry /Version trong catalog ở bản sửa đổi được nối thêm. Phần header vẫn giữ nguyên như đã ghi. /Version trong catalog được ưu tiên khi nó chỉ định một phiên bản mới hơn.
Tài liệu liên quan
Phần tiêu đề “Tài liệu liên quan”- PDF thực sự là gì — mô hình đối tượng và mục cross-reference duy nhất mà một bản cập nhật sẽ mở rộng.
- Chữ ký nằm ở đâu trong một tệp PDF — cơ chế dải byte mà cập nhật gia tăng tồn tại để bảo vệ.
- Xác thực một chữ ký đúng cách — những gì một quá trình xác thực đúng kiểm tra xuyên suốt lịch sử bản sửa đổi của một tệp.
Bảng thuật ngữ
Phần tiêu đề “Bảng thuật ngữ”- Cập nhật gia tăng — lưu thay đổi bằng cách nối thêm các đối tượng đã thay đổi, một mục cross-reference mới và một trailer mới vào cuối tệp, mà không thay đổi các byte hiện có.
/Prev— entry trong trailer (hoặc cross-reference stream) giữ byte offset của mục cross-reference trước đó. Nó liên kết các bản sửa đổi thành một chuỗi ngược.- Bản sửa đổi (Revision) — trạng thái của tệp được ghi lại bởi một mục cross-reference và trailer của nó. Một tệp có N mục cross-reference thì có N bản sửa đổi.
/ByteRange— mảng trong một signature dictionary cho biết hai đoạn byte mà digest của chữ ký bao phủ (mọi thứ trừ chính giá trị chữ ký).- Dải byte đã ký (Signed byte range) — đúng những byte mà digest của một chữ ký được tính trên đó. Cập nhật gia tăng tồn tại để những byte này không bao giờ bị dịch chuyển hay ghi đè.