涂黑不是一个黑色矩形
Spec: ISO 32000-2, §7.11ISO 32000-2 §7.11Spec: ISO 32000-2, §14.13.5ISO 32000-2 §14.13.5
一个画在名字上方的黑色矩形什么也藏不住。页面看起来像被涂黑了,但底下的文字仍然在那里,仍然可选取、仍然可复制。真正的涂黑并不覆盖内容。它删除内容。
本页解释装饰性覆盖与真正内容移除之间的落差、为何元数据与内嵌文件能在一次粗糙的作业中存活下来,以及 NextPDF 企业版 RedactionEngine 如何在一个区域它无法真正移除时拒绝假装它已经没了。
为何这很重要
标题为“为何这很重要”的章节被引用得最多的几起 PDF 数据泄露并非高明的攻击。它们是黑色矩形。有人在一段敏感段落上方画了一个填满的盒子、导出了文件,然后把它发了出去。收件人按住那个盒子、横拖过它,原文便径直从文档复制到了剪贴板上。
原因是结构性的。一个 PDF 页面是一组按顺序分层的绘图运算子。绘制一个不透明矩形是在最上面添加一个运算子;它并不移除底下那个显示文字的运算子。在眼睛看来,文字被藏起了。在机器看来,它仍恰好在它一向所在之处。那层覆盖是一件戏服,而戏服是会脱下来的。
这之所以代价高昂,是因为失败既隐形又终局。文档看起来正确、通过了视觉审查、发布了出去。泄露稍后才被某个选取了那段文字的人发现,而到那时每一份副本都已经流出去了。
简短版本
标题为“简短版本”的章节- 一个黑色盒子是一道分层叠在文字之上的绘图操作,而非对它的一次删除。那些字节存活了下来。
- 可选取的文字之所以存活,是因为页面内容流仍携带着绘出那些字的字形。
- 文档元数据之所以存活,是因为它住在一个独立的地方——document information 字典与 XMP 封包——那是页面上的任何矩形都搆不到的。
- 内嵌文件之所以存活,是因为一份 PDF 能把整份文件作为内嵌文件流携带在它内部。Spec: ISO 32000-2, §7.11ISO 32000-2 §7.11 页面上的一个标记永远不会碰到一个附件。
- 真正的涂黑是破坏性的。 它从字节流中、而非从图像中移除内容。没了就是没了,且那些残留面也一并被清除。
NextPDF 如何处理
标题为“NextPDF 如何处理”的章节装饰性对比破坏性,精确地说
标题为“装饰性对比破坏性,精确地说”的章节一层装饰性覆盖改变一页看起来如何。破坏性涂黑改变文档包含什么。前者可被任何有文本光标的人逆转。后者则什么也不留下可供挽回。
NextPDF 企业版 RedactionEngine 是为文本内容层级的破坏性案例而构建的。它的 redact() 方法接收内容、一份描述要找什么以及如何替换它的 RedactionPolicy,并返回一个 RedactionResult。在内部,它侦测那些敏感的跨度、把任何重叠的合并成不相交的覆盖范围,然后用 substr_replace 重写那串内容字符串,使每一个被覆盖的字节都恰好被移除一次。敏感文字没有被覆盖。它从结果带回的缓冲区中消失了。
那个结果对所发生之事是诚实的:它记录原始内容的 SHA-256 哈希值、被重写的 redactedContent、一份每个发现项的审计报告,以及一个 wasModified 标志。你可以证明那些字节变了,而且你可以证明是哪些。
你必须去追的那些残留
标题为“你必须去追的那些残留”的章节移除可见文字是必要的,但还不充分。那些相同的字,以及你在页面上从未看见的字,藏在页面层级编辑永远搆不到的地方。
一份文档把元数据携带了两遍:一份老旧的 information 字典与一个 XMP 封包,两者之一都可能持有一个作者名、一个标题,或一个工具指纹。它能把整份文件作为内嵌文件流携带。Spec: ISO 32000-2, §7.11ISO 32000-2 §7.11 它能把那些载荷作为关联文件绑定到页面结构上,那是一条页面不渲染、但文档仍持有的相关产物通道。Spec: ISO 32000-2, §14.13.5ISO 32000-2 §14.13.5 这些之中没有一个在你所涂黑的那一页上,因此没有一个会被一个矩形碰到。关于这一点的经典指引——NSA 那份“Hidden Data and Metadata in Adobe PDF Files”报告——之所以存在,正是因为太多真实的泄露来自那些残留,而非可见的页面。
一次涂黑,若删除了页面上的字、却把作者名留在 XMP 封包里、把原始电子表格留作一个附件,那它并没有涂黑那份文档。它涂黑了它的一个视图。
- DetectFind every sensitive span — visible text, and the entity types a policy targets — not just the region you happen to see.
- 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.
- Clear the residueStrip the information dictionary and XMP metadata, and remove embedded and associated files that carry the same payload off-page.
- VerifyConfirm no selectable text, metadata field, or attachment reproduces the redacted content before the file leaves the building.
失败封闭,而非假造它
标题为“失败封闭,而非假造它”的章节有两种涂黑方式:按内容(找到这个名字、删了它)以及按区域(涂掉页面上这个矩形,无论它底下是什么)。企业版引擎真实地做第一种。它刻意不假装去做第二种。
redact() 方法接受第三个参数,一份 ZoneRedaction 坐标区域的清单。文本内容引擎没有页面内容覆盖汇点——没有办法真正移除坐落在一个坐标盒子内部的东西。因此当那份清单非空时,它不会画一个矩形并把那个区域称为“已涂黑”。它会抛出一个类型化的 RuntimeException,并告诉你改为透过一条页面内容涂黑流水线来路由那个区域。
那次拒绝,正是本页被化作代码的整个要点。引擎宁可停下来并指明它做不到什么,也不愿为一个它原封未动留下的区域返回一个“已涂黑”的发现项。一层被作为移除上报的无声装饰性覆盖,正是那种会酿成泄露的虚假保证。失败封闭把那次泄露变成一条你在发出之前就看见的错误消息。
常见的误解
标题为“常见的误解”的章节“我在它上面画了一个黑色盒子,所以它被涂黑了。”
这是陷阱,而它是整个格式中最常见的一个。一个填满的矩形是一道叠在文字之上的绘图指令。那些显示文字的运算子仍在内容流里、那些字形仍在绘制,而一次选取或一次复制会原封不动地把它们读出来。你藏了一张图;你并没有改变一份文档。
第二个、更微妙的陷阱:“我删了页面上的文字,所以文档是干净的。”还没。那些相同的内容,以及你从未显示过的内容,可以在元数据与内嵌文件中存续。涂黑唯有在每一个面——页面内容、元数据,以及附件——都已清除你着手移除之物时,才算完成。
限制与边界
标题为“限制与边界”的章节NextPDF 在此处所做之事的诚实范畴,按版本划分:
- Core 根本没有文档涂黑。 它配备一个针对审计日志 PII 的隐私清理器——把敏感值挡在你自己的诊断记录之外——而那与涂黑一份已交付的 PDF 完全是两回事。如果你用的是 Core 且你需要涂黑一份文档,你想要的能力并不在场。别画一个盒子就假定引擎移除了任何东西。
- 企业版
RedactionEngine涂黑文本内容,破坏性地。 它重写内容缓冲区以移除敏感字节,并报告一个哈希值、一份审计报告,以及一个wasModified标志,因此那次移除是可被证明的。 - 坐标区域涂黑失败封闭。 传入一份非空的
ZoneRedaction清单,引擎便会抛出,而非假造一层覆盖。那是一项刻意的安全性质,而非一个缺失的功能:一个它无法真正清除的区域,就是一个它拒绝称为已清除的区域。 - 清除每一个残留面也是操作者的责任。 移除可见文字只是一步。一次完整的作业还会剥除 information 字典、XMP 封包,以及任何携带同样载荷而离开页面的内嵌或关联文件(Spec: ISO 32000-2, §7.11ISO 32000-2 §7.11、Spec: ISO 32000-2, §14.13.5ISO 32000-2 §14.13.5)。
| Edition | Availability |
|---|---|
| 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. |
| Pro | Not 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. |
相关文档
标题为“相关文档”的章节- PDF 加密实际如何运作 — 为何加密一个文件与从中移除内容并不是同一回事,以及两者各自在何处保护你。
- 元数据与 XMP 封包 — 页面层级涂黑永远搆不着的那个残留面,详述之。
- 一个拒绝猜测的 API — 涂黑引擎对一个它无法完成的区域,所套用的同一套失败封闭立场。
词汇表
标题为“词汇表”的章节- 装饰性覆盖 — 一道绘图操作,例如一个填满的矩形,分层叠在内容之上以在视觉上将其藏起。它并不移除底下的内容,那些内容仍可选取、可复制。
- 破坏性涂黑 — 从文档的字节流中、而非从图像中移除内容。一旦完成,底下便没有任何东西可供挽回。
- 内容流 — 绘制一页的那份有序绘图运算子清单,包括绘出字形的显示文字运算子。叠在最上层的一个盒子并不会删除它底下的文字运算子。
- 残留面 — 文档中一个保有页面所不显示之内容的地方:information 字典、XMP 元数据封包,以及内嵌或关联文件。一次完整的涂黑会清除它们全部。
- 内嵌文件流 — 一份携带于 PDF 内部的整份文件(ISO 32000-2 §7.11)。一个页面上的标记永远不会碰到一个附件。
- 失败封闭 — 当一项操作无法被真实地完成时,以一个类型化错误停下来,而非产生一个看似可信、实则错误的结果。企业版引擎对它无法完成的坐标区域涂黑正是这么做。