跳转到内容
getnextpdf.com

Enterprise 版本

内容解除与重构(CDR)

NextPDF Enterprise 通过内容解除与重构(CDR)来中和一份不受信任的 PDF:它解析文件、对照接纳限制进行检查、检测危险特性、移除它们,并从剩余的对象重建一个新文件。这些危险特性是活动内容——内嵌脚本、启动动作、内嵌文件、富媒体等等——查看器可能会执行或据此采取动作。本页为行为层面:它说明引擎接纳什么、剥离什么、重建什么,以及为什么其输出不是一份证据性副本。

CDR 是一种破坏性、单向的变换。重建后的输出是输入的一份安全投影,而非一份被保存的或哈希等价的副本。该边界陈述于安全与合规

前提条件已在前置数据中陈述,并在前提条件处重述。

该能力随 NextPDF Enterprisenextpdf/enterprise)发行,并以一份 Enterprise 层级的授权信封激活。没有该授权的部署不会加载该能力的类。NextPDF Core 与 NextPDF Pro 不提供 CDR。引擎在进程内运行;不受信任的文档在你的主机上被解析与重建。比较各版本并获取授权

PDF 支持查看器可以执行或据此采取动作的活动内容。依据 ISO 32000-2 §12.6.4,一个 ECMAScript 动作会使 PDF 处理器执行一段可以改变字段值与外观的脚本。依据 ISO 32000-2 §12.6.4,一个启动动作会启动一个应用程序,或打开或打印一份文档。依据 ISO 32000-2 §7.7.4,名称字典中的文档级脚本会在文档被打开时自动运行。CDR 移除这些面。

引擎运行一条有序的管线:

  1. 解析输入 PDF。
  2. 接纳控制 —— 验证文档处于接纳边界之内(对象数量、页数、解码后的流大小,以及一个用于防御解压炸弹输入的流膨胀比)。处于限制之外的文档会被拒绝,而非被清洗。该拒绝与清洗结果分别报告,以便你的错误处理能区分两者。
  3. 检测威胁 —— 扫描所配置的威胁类型。
  4. 过滤 —— 把对象划分为安全集与移除集。
  5. 引用清洗 —— 清理因移除而悬空的指针。
  6. 重建 —— 把安全对象序列化进一个新的 PDF。

可检测的威胁类型包括内嵌脚本(以及能够捕获畸形结构中脚本的激进脚本剥离)、附加动作与打开动作触发器、启动动作、远程跳转动作、表单提交与导入数据动作、内嵌文件、富媒体与三维内容、文档级脚本名称树、外部统一资源标识符(URI)动作、XML 表单架构数据、交互式表单字段、非默认的可选内容图层,以及数字签名对象(一次重建必然会使其失效,因为字节范围不再匹配)。无法被解析的对象本身被视为一种潜在绕过并被移除。

三种策略设定默认值:

  • Standard —— 移除引擎可检测、已配置的活动内容威胁类型;增量更新始终被扁平化以防御签名遮蔽,且该扁平化在此模式下不可配置。
  • Paranoid —— 相同的移除,但资源限制更严。
  • Permissive —— 用于受信任的来源:移除脚本与启动动作,但保留 URI 动作,并使增量更新扁平化可配置。

CDR 从它判定为安全的对象重建一个新文件,而不是原地删除危险的键。这种允许列表(allow-list)姿态是承重性的决策。检测器从未识别的对象——包括它无法解析的对象——不会存续进入输出,因此一个未知结构会失败关闭(fail closed),而不是从拒绝列表(deny-list)的缝隙中溜过去。接纳控制出于同样的原因先行运行:超限或无法解析的输入会被直接拒绝,绝不部分重建。这份安全的代价是保存性。重建会改变字节布局、使签名失效,并丢弃其威胁类型被剥离的载荷——这正是为什么其输出是一份安全投影,而绝非一份证据性副本。

设计背景:清洗不受信任的 PDF:内容解除与重构

  1. 安装 NextPDF Core 与 Enterprise 包,并持有一份有效的 Enterprise 授权。
  2. 为你的输入定义接纳边界——最大对象数量、页数、解码后的流大小,以及膨胀比——或者使用某个预设策略的限制。
  3. 决定策略:用于一般不受信任接收的 standard、用于最严格姿态的 paranoid,或仅用于受信任来源的 permissive。

策略是不可变的,并携带:

  • 要移除的威胁类型 —— 检测器扫描、重建器剥离的那一组。
  • URI 动作处理 —— 是否保留外部 URI 动作。
  • 增量更新扁平化 —— 在 standard 与 paranoid 中始终开启;在 permissive 中可配置。
  • 接纳限制 —— 最大对象数量、页数、解码后的流大小,以及膨胀比。这些是接纳边界,而非清洗行为;超过它们会拒绝该文档。
  1. 读取不受信任的 PDF 字节。
  2. 选择一个策略(standard、paranoid 或 permissive),或用显式限制构建一个。
  3. 用这些字节与该策略运行引擎的清洗操作。
  4. 检视结果:如果它报告未被接纳,请呈现拒绝原因;否则使用重建后的字节与已移除威胁的列表。
  5. 把重建后的输出当作一份经清洗的投影,绝不要把它当作输入的证据性或归档性副本。
examples/enterprise/cdr-sanitize-guarded.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Enterprise\Security\Cdr\CdrEngine;
use NextPDF\Enterprise\Security\Cdr\CdrPolicy;
use NextPDF\Enterprise\Security\Cdr\CdrResult;
use Psr\Log\LoggerInterface;
final readonly class UntrustedPdfIntake
{
public function __construct(
private CdrEngine $engine,
private LoggerInterface $logger,
) {}
/**
* Sanitize an untrusted PDF and distinguish rejection from a clean rebuild.
*
* An over-limit document is rejected by admission control, not sanitized;
* the caller surfaces the rejection reason instead of trusting an empty
* output. The rebuilt bytes are a security projection, never an
* evidentiary copy of the input.
*
* @param string $pdfBytes The untrusted PDF bytes.
*
* @return CdrResult The sanitization result, including admission status.
*/
public function sanitize(string $pdfBytes): CdrResult
{
$result = $this->engine->sanitize($pdfBytes, CdrPolicy::standard());
if (! $result->admitted) {
$this->logger->warning('PDF rejected by CDR admission control', [
'reason' => $result->rejectionReason,
]);
return $result;
}
$this->logger->info('PDF sanitized', [
'removedThreats' => \count($result->removedThreats),
'originalSize' => $result->originalSize,
'sanitizedSize' => $result->sanitizedSize,
]);
return $result;
}
}
  1. 运行一个携带已知活动内容面的样本(例如一段文档级脚本),并确认它出现在已移除威胁的列表中,且在重建后的输出中缺失。
  2. 运行一个超限文档,并确认结果报告未被接纳并附带拒绝原因,且重建后的字节为空。
  3. 确认一份带数字签名的输入在重建中其签名对象被移除——一次重建会使原始签名失效,因为字节范围改变了,所以失效的签名对象按设计被剥离。
  4. 在查看器中打开重建后的输出,并确认打开时没有脚本执行。
  • 不用于证据。 重建后的输出是一份破坏性、不可逆的投影。请不要把它用于法律证据保全、用于与原文件做哈希比对,或用作归档副本。如果你需要一份证据性副本,请将原文件置于单独的保管之下。
  • 接纳先于清洗。 资源限制是一个接纳边界;超限文档会被拒绝,而非被清洗。请分别报告拒绝,使一个超限输入绝不会被误认为一次干净的重建。
  • 活动内容被移除。 脚本、启动动作、文档级脚本名称树、内嵌文件、富媒体等面均被剥离——也就是查看器原本会执行的那些动作(ISO 32000-2 §12.6.4 ECMAScript 动作;启动动作;文档级脚本 §7.7.4)。
  • 签名因重建而失效。 因为重建改变了字节布局,原始签名将不再能通过验证;签名对象被移除,而非误导性地原地保留。
  • 按设计有损。 当其威胁类型被剥离时,内嵌文件载荷——包括诸如 Factur-X 这样的发票附件——会被移除。CDR 是一份安全投影,而非一个保存层。

本页涉及文档安全。每一项规范性来源都以转述方式呈现;不复现任何规范性原文。

  • 解析失败。 无法被解析的文档会产生一个未被接纳的结果并附带解析错误原因,而不是一次部分重建。
  • 超限输入。 结果报告未被接纳并附带一个针对具体限制的原因;重建后的字节为空。
  • 无法解析的对象。 无法被解析的对象被视为一种潜在绕过并被移除,而非静默保留。
  • 尽力而为的页数。 页数接纳检查是尽力而为;一个畸形的 catalog 不会使管线崩溃,但对象数量与流限制仍然适用。

本页仅记录外部可观察的行为与受支持的公共 API 接口。内部命名空间路径、辅助类、机制表、运行手册文件名,以及工单前缀均不在范围之内。