跳转到内容
getnextpdf.com

涂黑不是一个黑色矩形

Spec: ISO 32000-2, §7.11Spec: ISO 32000-2, §14.13.5

一个画在名字上方的黑色矩形什么也藏不住。页面看起来像被涂黑了,但底下的文字仍然在那里,仍然可选取、仍然可复制。真正的涂黑并不覆盖内容。它删除内容。

本页解释装饰性覆盖与真正内容移除之间的落差、为何元数据与内嵌文件能在一次粗糙的作业中存活下来,以及 NextPDF 企业版 RedactionEngine 如何在一个区域它无法真正移除时拒绝假装它已经没了。

被引用得最多的几起 PDF 数据泄露并非高明的攻击。它们是黑色矩形。有人在一段敏感段落上方画了一个填满的盒子、导出了文件,然后把它发了出去。收件人按住那个盒子、横拖过它,原文便径直从文档复制到了剪贴板上。

原因是结构性的。一个 PDF 页面是一组按顺序分层的绘图运算子。绘制一个不透明矩形是在最上面添加一个运算子;它并不移除底下那个显示文字的运算子。在眼睛看来,文字被藏起了。在机器看来,它仍恰好在它一向所在之处。那层覆盖是一件戏服,而戏服是会脱下来的。

这之所以代价高昂,是因为失败既隐形又终局。文档看起来正确、通过了视觉审查、发布了出去。泄露稍后才被某个选取了那段文字的人发现,而到那时每一份副本都已经流出去了。

  • 一个黑色盒子是一道分层叠在文字之上的绘图操作,而非对它的一次删除。那些字节存活了下来。
  • 可选取的文字之所以存活,是因为页面内容流仍携带着绘出那些字的字形。
  • 文档元数据之所以存活,是因为它住在一个独立的地方——document information 字典与 XMP 封包——那是页面上的任何矩形都搆不到的。
  • 内嵌文件之所以存活,是因为一份 PDF 能把整份文件作为内嵌文件流携带在它内部。Spec: ISO 32000-2, §7.11 页面上的一个标记永远不会碰到一个附件。
  • 真正的涂黑是破坏性的。 它从字节流中、而非从图像中移除内容。没了就是没了,且那些残留面也一并被清除。

一层装饰性覆盖改变一页看起来如何。破坏性涂黑改变文档包含什么。前者可被任何有文本光标的人逆转。后者则什么也不留下可供挽回。

NextPDF 企业版 RedactionEngine 是为文本内容层级的破坏性案例而构建的。它的 redact() 方法接收内容、一份描述要找什么以及如何替换它的 RedactionPolicy,并返回一个 RedactionResult。在内部,它侦测那些敏感的跨度、把任何重叠的合并成不相交的覆盖范围,然后用 substr_replace 重写那串内容字符串,使每一个被覆盖的字节都恰好被移除一次。敏感文字没有被覆盖。它从结果带回的缓冲区中消失了。

那个结果对所发生之事是诚实的:它记录原始内容的 SHA-256 哈希值、被重写的 redactedContent、一份每个发现项的审计报告,以及一个 wasModified 标志。你可以证明那些字节变了,而且你可以证明是哪些。

移除可见文字是必要的,但还不充分。那些相同的字,以及你在页面上从未看见的字,藏在页面层级编辑永远搆不到的地方。

一份文档把元数据携带了两遍:一份老旧的 information 字典与一个 XMP 封包,两者之一都可能持有一个作者名、一个标题,或一个工具指纹。它能把整份文件作为内嵌文件流携带。Spec: ISO 32000-2, §7.11 它能把那些载荷作为关联文件绑定到页面结构上,那是一条页面不渲染、但文档仍持有的相关产物通道。Spec: ISO 32000-2, §14.13.5 这些之中没有一个在你所涂黑的那一页上,因此没有一个会被一个矩形碰到。关于这一点的经典指引——NSA 那份“Hidden Data and Metadata in Adobe PDF Files”报告——之所以存在,正是因为太多真实的泄露来自那些残留,而非可见的页面。

一次涂黑,若删除了页面上的字、却把作者名留在 XMP 封包里、把原始电子表格留作一个附件,那它并没有涂黑那份文档。它涂黑了它的一个视图。

  1. DetectFind every sensitive span — visible text, and the entity types a policy targets — not just the region you happen to see.
  2. Delete, do not coverRewrite the content stream so the glyphs are removed from the bytes. An opaque box on top leaves the text underneath intact.
  3. Clear the residueStrip the information dictionary and XMP metadata, and remove embedded and associated files that carry the same payload off-page.
  4. VerifyConfirm no selectable text, metadata field, or attachment reproduces the redacted content before the file leaves the building.
What a complete destructive redaction removes, in order: detect the sensitive content, delete the bytes from the content stream rather than draw over them, then clear the residue surfaces — metadata and embedded payloads — that a page-level mark never reaches, and finally verify nothing copyable survived.

有两种涂黑方式:按内容(找到这个名字、删了它)以及按区域(涂掉页面上这个矩形,无论它底下是什么)。企业版引擎真实地做第一种。它刻意假装去做第二种。

redact() 方法接受第三个参数,一份 ZoneRedaction 坐标区域的清单。文本内容引擎没有页面内容覆盖汇点——没有办法真正移除坐落在一个坐标盒子内部的东西。因此当那份清单非空时,它不会画一个矩形并把那个区域称为“已涂黑”。它会抛出一个类型化的 RuntimeException,并告诉你改为透过一条页面内容涂黑流水线来路由那个区域。

那次拒绝,正是本页被化作代码的整个要点。引擎宁可停下来并指明它做不到什么,也不愿为一个它原封未动留下的区域返回一个“已涂黑”的发现项。一层被作为移除上报的无声装饰性覆盖,正是那种会酿成泄露的虚假保证。失败封闭把那次泄露变成一条你在发出之前就看见的错误消息。

“我在它上面画了一个黑色盒子,所以它被涂黑了。”

这是陷阱,而它是整个格式中最常见的一个。一个填满的矩形是一道叠在文字之上的绘图指令。那些显示文字的运算子仍在内容流里、那些字形仍在绘制,而一次选取或一次复制会原封不动地把它们读出来。你藏了一张图;你并没有改变一份文档。

第二个、更微妙的陷阱:“我删了页面上的文字,所以文档是干净的。”还没。那些相同的内容,以及你从未显示过的内容,可以在元数据与内嵌文件中存续。涂黑唯有在每一个——页面内容、元数据,以及附件——都已清除你着手移除之物时,才算完成。

NextPDF 在此处所做之事的诚实范畴,按版本划分:

  • Core 根本没有文档涂黑。 它配备一个针对审计日志 PII 的隐私清理器——把敏感值挡在你自己的诊断记录之外——而那与涂黑一份已交付的 PDF 完全是两回事。如果你用的是 Core 且你需要涂黑一份文档,你想要的能力并不在场。别画一个盒子就假定引擎移除了任何东西。
  • 企业版 RedactionEngine 涂黑文本内容,破坏性地。 它重写内容缓冲区以移除敏感字节,并报告一个哈希值、一份审计报告,以及一个 wasModified 标志,因此那次移除是可被证明的。
  • 坐标区域涂黑失败封闭。 传入一份非空的 ZoneRedaction 清单,引擎便会抛出,而非假造一层覆盖。那是一项刻意的安全性质,而非一个缺失的功能:一个它无法真正清除的区域,就是一个它拒绝称为已清除的区域。
  • 清除每一个残留面也是操作者的责任。 移除可见文字只是一步。一次完整的作业还会剥除 information 字典、XMP 封包,以及任何携带同样载荷而离开页面的内嵌或关联文件(Spec: ISO 32000-2, §7.11Spec: ISO 32000-2, §14.13.5)。
Document redaction — edition availability
EditionAvailability
Core

Not in this edition. Core has no document redaction. It provides only an audit-log PII sanitiser, which keeps sensitive values out of your own diagnostic records — it does not redact a delivered PDF.

ProNot in this edition
Enterprise

Available. The enterprise RedactionEngine performs destructive text-content redaction and fails closed — it throws a typed exception — on coordinate-zone redaction it cannot truly fulfil, rather than painting a cosmetic overlay and reporting a region as removed.

  • 装饰性覆盖 — 一道绘图操作,例如一个填满的矩形,分层叠在内容之上以在视觉上将其藏起。它并不移除底下的内容,那些内容仍可选取、可复制。
  • 破坏性涂黑 — 从文档的字节流中、而非从图像中移除内容。一旦完成,底下便没有任何东西可供挽回。
  • 内容流 — 绘制一页的那份有序绘图运算子清单,包括绘出字形的显示文字运算子。叠在最上层的一个盒子并不会删除它底下的文字运算子。
  • 残留面 — 文档中一个保有页面所不显示之内容的地方:information 字典、XMP 元数据封包,以及内嵌或关联文件。一次完整的涂黑会清除它们全部。
  • 内嵌文件流 — 一份携带于 PDF 内部的整份文件(ISO 32000-2 §7.11)。一个页面上的标记永远不会碰到一个附件。
  • 失败封闭 — 当一项操作无法被真实地完成时,以一个类型化错误停下来,而非产生一个看似可信、实则错误的结果。企业版引擎对它无法完成的坐标区域涂黑正是这么做。