跳转到内容
getnextpdf.com

Enterprise 版本

取证

NextPDF Enterprise Forensics 读取一份 PDF 的增量更新历史,并生成一份结构化、只读的报告,涵盖各修订版、被归类的事件,以及按对象的变化。它支持取证分析工作流程。它不是一个防篡改封印,也不断言一份文档是真实的或未经改动的。

本能力随 NextPDF Enterprisenextpdf/enterprise)发行,并以一个 Enterprise 层级的授权封套激活。没有该权益的部署不会加载本能力的类。比较版本并获取授权

Terminal window
composer require nextpdf/enterprise:^3

一份 PDF 可以通过把变化追加到文件末尾来更新,而非重写它。每次更新都增加一个新的交叉引用区与一个新的尾部,而原始字节被原地保留——ISO 32000-2:2020 §7.5.6。当一个对象被改动时,更新会追加一个新副本,且更新的交叉引用区记录一个覆盖较旧偏移量的字节偏移量;一个读取器会解析出最新的副本——ISO 32000-2:2020 §7.5.6。初始文件结构可能被后续更新修改——ISO 32000-2:2020 §7.5.4。一次更新的交叉引用区仅列出在该次更新中被新增、修改或删除的对象——ISO 32000-2:2020 §7.5.5。

分析器读取这个分层结构。它解析每个修订版的交叉引用表、推导出修订版的字节边界,并把每个修订版的条目与下一个更旧的修订版做比对,以把每个对象归类为新增、修改或删除。然后它把对象变化归集为更高层级的事件:增加了一个签名、文档 catalog 被更新、出现了一个加密字典,或者一组对象被新增、修改或移除。输出是一个报告对象,携带一个修订版计数、总大小、一个按修订版的摘要列表、一条被归类的事件时间线,以及按对象的变化列表。

分析器是只读的。它从结构性标记中检测一个签名修订版的存在;它不验证任何签名、不重新计算任何摘要,也不检查任何证书。签名验证是一项独立的 Core 能力。一份生成的报告是对所解析更新历史的一种结构性描述;它不是关于一份文档真实、某次变化未经授权,或每一处修改都被检测到的判定。请把该报告当作“按测试所及、对照解析器对修订链的视角所做的防篡改检测”,而非一项取证保证或一份法庭可采的鉴证。

分析器刻意止步于结构。它报告修订链所包含的内容,而绝不断言某次变化经过授权或某个签名有效。结构性存在与密码学有效性是不同的主张;把它们合并会让调用方把防篡改证据误当作一项保证。因此签名有效性仍归属于单一的 Core 签名接口,Forensics 与之组合而非重复它。该报告是可 JSON 序列化的结构性元数据,因此 SIEM 无需触及文档内容即可摄取编辑历史。 设计背景:增量更新及其重要性

类型种类角色稳定性起始版本
ForensicAnalyzerclass解析一份 PDF 并返回一份取证报告(静态 analyzestable1.10.0
ForensicReportclass分析结果;为 SIEM 导出而 JsonSerializablestable1.10.0
RevisionSummaryclass按修订版的事实:对象计数、大小、字节边界、存在标志stable1.10.0
ForensicEventclass一个带受影响对象列表的被归类事件stable1.10.0
ForensicEventTypeenum事件类别(增加了签名、catalog 被更新、新增了对象等)stable1.10.0
ObjectChangeclass一个对象跨两个修订版的变化记录stable1.10.0
ObjectChangeTypeenumAdded、Modified 或 Deletedstable1.10.0

ForensicReport 暴露 hasIncrementalUpdates()hasAnySignature()getEventsByType()getChangesForRevision()jsonSerialize()。修订版摘要上的 hasSignature 标志是一个结构性存在信号,而非一个有效性结果。

Analyze the revision history of a PDF
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Forensics\ForensicAnalyzer;
/**
* Produce a forensic report from PDF bytes.
*
* @param string $pdfData Raw PDF file bytes.
*
* @return array{revisions: int, incremental: bool, signedRevisionPresent: bool}
*/
function inspect(string $pdfData): array
{
$report = ForensicAnalyzer::analyze($pdfData);
return [
'revisions' => $report->revisionCount,
'incremental' => $report->hasIncrementalUpdates(),
'signedRevisionPresent' => $report->hasAnySignature(),
];
}

hasAnySignature() 报告结构中存在一个签名修订版。它并不陈述该签名是有效的。

Forensic analysis with SIEM export and fail-closed handling
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Forensics\ForensicAnalyzer;
use NextPDF\Enterprise\Forensics\ForensicEventType;
use Psr\Log\LoggerInterface;
final readonly class RevisionAuditor
{
public function __construct(private LoggerInterface $logger) {}
/**
* Analyze a document and emit a structural JSON record for the SIEM.
*
* @param string $pdfData The PDF bytes to inspect.
*
* @return string A JSON forensic report (no document content).
*/
public function audit(string $pdfData): string
{
try {
$report = ForensicAnalyzer::analyze($pdfData);
$this->logger->info('Forensic analysis complete', [
'revisions' => $report->revisionCount,
'sizeBytes' => $report->totalSizeBytes,
'signatureAddedEvents' => count(
$report->getEventsByType(ForensicEventType::SignatureAdded),
),
]);
return json_encode($report, JSON_THROW_ON_ERROR);
} catch (\Throwable $e) {
$this->logger->error('Forensic analysis failed', ['error' => $e->getMessage()]);
throw $e;
}
}
}

该日志记录仅携带计数与大小。它不携带文档文本。catch 块会重新抛出;它不吞掉一次解析失败。

  • 一份单修订版文档没有增量历史。其变化列表为空;这不是真实性的证据。
  • 一个 SignatureAdded 事件意味着一个签名修订版在结构上存在。它不是一个签名有效性结果。请用 Core 签名接口验证该签名。
  • 对象复用是正常的:一个被更新的对象保留其对象号,并追加一个新副本。分析器把这报告为 Modified,而非移除并重新创建。
  • 一次 Deleted 归类是交叉引用链中的一次空闲条目转变。一个读取器仍可能解析出该对象的一个更旧副本;结构层级上的删除并不保证不可恢复性。
  • 分析器报告解析器所观察到的。一份被特意构造以迷惑解析器的文档,可能产生一份与另一个工具的视角不一致的报告。该报告不是关于每一处修改都被检测到的主张。
  • 输入是有界的。一份过大或修订版过多的文档会以一个有类型的解析异常失败关闭,而非消耗无界内存。

分析成本随修订版计数与对象计数而变化,而非随渲染的页面复杂度而变化。1500 ms 的墙钟预算涵盖一份典型的多修订版业务文档。复现性配置为 structural:对于给定输入,报告是确定性的,但绝对字节偏移量反映的是确切的输入文件,且在重新保存的副本之间不可移植。

分析器是只读的,绝不写入输入。它是一个分析性接口,而非一个变换性接口。它检测签名与加密标记的存在,但不执行任何密码学运算,因此它不作任何 FIPS 主张。一份报告描述所解析的更新历史;它不是一项真实性断言,且绝不能被呈现为防篡改的、取证上有保证的,或法庭可采的。结论由操作方得出;库报告的是结构。

分析在持有该 PDF 的主机上、于进程内运行。没有任何文档内容离开主机。报告携带对象号、修订版索引、大小、字节边界,以及事件类别——结构性元数据,而非文档文本或被检测到的个人数据。输入 PDF 或报告本身是否包含个人数据,以及各自存储在何处,是一项处于库边界之外的部署责任。

库抛出带结构性消息的有类型异常,且不把文档字节放入异常文本。一个围绕分析进行记录的部署,应记录报告的计数与类别——如生产示例所示——且绝不能把原始 PDF 载荷记录到日志或一个 APM 后端。JSON 报告是可安全转发给 SIEM 的工件。

本模块中不发生任何密码学运算,因此没有任何 FIPS 模式特定行为。签名验证(它是密码学的)是一项独立的 Core 能力,并在那里记录。

主张标准条款
后续更新把附加元素追加到文件末尾;初始结构被后续更新修改。ISO 32000-2:2020§7.5.6
一个被更新的对象作为一个新副本被追加,且更新的交叉引用条目覆盖先前的字节偏移量;读取器解析出最新的副本。ISO 32000-2:2020§7.5.6
初始文件结构可能被后续更新修改。ISO 32000-2:2020§7.5.4
一次更新的交叉引用区仅包含针对被新增、修改或删除对象的条目。ISO 32000-2:2020§7.5.5
签名字典记录了什么被签名。ISO 32000-2:2020§12.8.1
ByteRange 定义签名所覆盖的字节跨度(签名验证是一项独立的 Core 能力)。ISO 32000-2:2020§12.8.1
一个文档安全存储在后续的一个修订版中持有长期验证素材。ISO 32000-2:2020§12.8.4

所有条款均为转述。NextPDF 不复现规范性原文。权威措辞请查阅已发布的标准。NextPDF 不作出任何取证认证主张;该报告描述所解析的更新结构,而非对文档完整性的一项经认证的判定。

  • 分析器是只读的:它绝不写入输入文档,且不执行任何密码学运算。
  • 它解析每个修订版的交叉引用表、推导出修订版的字节边界,并把每个对象归类为新增、修改或删除,然后把变化归集为一条被归类的事件时间线。
  • 一个 SignatureAdded 事件意味着一个签名修订版在结构上存在;它不是一个签名有效性结果——验证是一项独立的 Core 能力。
  • 一份单修订版文档具有一个空的变化列表(不是真实性的证据);一次 Deleted 归类是一次空闲条目转变,而非保证的不可恢复性。
  • 输入是有界的:一份过大或修订版过多的文档会以一个有类型的解析异常失败关闭。该报告是按测试所及的防篡改检测,而非一项取证保证或一份法庭可采的鉴证。

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

NextPDF Core(Apache-2.0)没有修订历史取证分析器——一项都没有;此能力在 Core 层级没有等价物。Core 提供权威的签名验证接口,分析器与之组合,但并不替代它。

NextPDF Pro 没有修订历史取证分析器——一项都没有;此能力在 Pro 层级没有等价物。只读的修订版与按对象变化报告,以及可 JSON 序列化的 SIEM 报告,仅随 nextpdf/enterprise 包发行。

修订版解析器、变化归类,以及事件时间线,在行为层面进行描述。解析器内部机制与任何内部归类细节不属于公开接口范围。签名有效性被刻意排除在此处范围之外——它是 Core 签名接口的职责。

分析在持有该 PDF 的主机上、于进程内运行;没有任何文档内容离开主机。输入 PDF 或报告是否包含个人数据,以及各自存储在何处,是一项处于库边界之外的部署责任。操作方从报告中得出结论;库报告结构,且不断言文档真实性。

Forensics 接口不适用任何出口管制限制。该报告绝不能被呈现为防篡改的、取证上有保证的,或法庭可采的。本文档不是法律意见;请咨询你自己的合规与法务顾问。