跳转到内容

NextPDF Laravel 包的安全与运维

该包会应用一组固定响应头,清洗下载文件名,在 worker 上校验队列输出路径,并通过可感知请求伪造的客户端封装发往时间戳颁发机构的 HTTP 请求。本页说明威胁模型,以及每项控制所需的部署配置。

Terminal window
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

该包将一个 PDF 引擎适配到 Web Framework(框架)中。信任边界包括 HTTP 请求和队列传输。以下控制措施覆盖响应处理、反序列化的任务负载,以及发往时间戳颁发机构的出站 HTTP。

资产威胁本包中的控制措施所需的部署配置
PDF HTTP 响应内容类型嗅探、点击劫持、Index(索引)收录每个 PdfResponse 工厂都会设置固定的响应头集合无;响应头不可配置
下载文件名响应头注入、路径遍历,位于 Content-Disposition文件名清洗器会剥除分隔符、控制字符和空字节无;清洗器始终运行
队列任务输出路径借由被篡改的序列化负载进行任意文件写入在 worker 上的 handle() 中校验路径将输出导向受控的存储路径
出站 TSA HTTP服务端请求伪造、明文篡改能感知请求伪造的 HTTP 客户端;除非显式放宽,否则强制使用 HTTPS保持 tsa.allow_insecure_http = false;固定 SPKI
共享的 worker 状态长生命周期 worker 中的跨请求状态泄漏锁定的字体注册表;有界的图像缓存;绑定到工厂的文档设置 preload_fonts;在容器层面限制内存

每个 PdfResponse 工厂都会设置一组固定的响应头:

  • Cache-Control: private, max-age=0, must-revalidate
  • Pragma: public
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Content-Security-Policy: default-src 'none'
  • X-Robots-Tag: noindex, nofollow
  • Referrer-Policy: no-referrer

这些值是 PdfResponse 中的常量,且不可配置。该包的测试套件会针对每个工厂方法(包括流式变体)断言每个响应头。

下载文件名在写入 Content-Disposition 响应头之前,会先经过清洗器处理。清洗器会移除路径分隔符、控制字符和空字节,并为非 ASCII 名称生成一个 RFC 5987 filename*= 参数。空文件名会变成 document.pdf

GeneratePdfJob 会将一个闭包序列化到队列传输上。输出路径会在 worker 上的 handle() 内部校验,而不是在分发时校验。该校验会拒绝:

  • 路径中的空字节,
  • 流包装器协议(例如 php://),
  • .. 路径遍历片段,
  • 任何不以 .pdf 结尾的路径(不区分大小写)。

每次拒绝都会抛出 InvalidArgumentException。校验会在消费负载的那一刻运行。Redis 或数据库传输中的序列化负载,可能在 worker 读取它之前被篡改。将输出路径导向受控的存储目录;不要从未经校验的请求输入派生该路径。

配置了时间戳颁发机构后,该包会绑定一个 PSR-18 Psr\Http\Client\ClientInterface。PSR-18 客户端发送 PSR-7 请求并返回 PSR-7 响应(PSR-18 §2)。绑定的客户端使用一个能感知请求伪造的层,封装基于 curl 的客户端;除非 tsa.allow_insecure_http 显式为 true,否则该层会强制使用 HTTPS。

时间戳颁发机构是一项 Premium 层级的能力。此处所述的 Core 包负责 HTTP 客户端绑定与时间戳客户端装配;签名本身需要 nextpdf/premium。本页不说明 PAdES 在 B-B 之上的基线行为;更高的基线不在范围内。

时间戳颁发机构的运维指引:

  1. 在生产环境中保持 tsa.allow_insecure_http 设置为 false
  2. tsa.pinned_public_keys 设置为时间戳颁发机构证书的 base64 SHA-256 SPKI 哈希(RFC 7469 形式)。
  3. 保持 tsa.warn_on_key_rotation 设置为 true,这样一来,在固定证书过期之前,SPKI 变更会被记录下来。
  4. 仅从受信任的配置读取 tsa.url。如果运维人员能够通过管理界面设置它,则应应用出站防火墙或 DNS 策略,以降低请求伪造的暴露面。

使用 Psr\Log\LoggerInterface 进行诊断。传入结构化上下文,而不是插值后的字符串。PSR-3 将 placeholder(占位符)的转义留给日志记录器实现处理,并指示调用方不要预先转义上下文值(PSR-3 §1.2)。记录异常类,而不是消息或堆栈跟踪,以减少日志中的内部细节。

resource: config/nextpdf.php (tsa hardening) + src/Laravel/NextPdfServiceProvider.php
<?php
declare(strict_types=1);
// .env — production timestamp-authority hardening
// NEXTPDF_TSA_URL=https://tsa.example.test
// NEXTPDF_TSA_ALLOW_INSECURE_HTTP=false
// NEXTPDF_TSA_WARN_ROTATION=true
return [
'tsa' => [
'url' => env('NEXTPDF_TSA_URL'),
'allow_insecure_http' => env('NEXTPDF_TSA_ALLOW_INSECURE_HTTP', false),
'warn_on_key_rotation' => env('NEXTPDF_TSA_WARN_ROTATION', true),
'pinned_public_keys' => [
// base64 SHA-256 SPKI hashes of the TSA certificate
],
],
];
  • 响应头集合是固定的。需要不同 CSP 的应用必须在工厂返回响应之后对其进行后处理。
  • 路径校验在 worker 上运行。错误的路径会通过 dispatch(),只有在任务执行时才会失败。
  • tsa.allow_insecure_http = true 会移除 HTTPS 强制要求,并削弱时间戳的信任度。仅将它限制在本地开发中使用。
  • 字体注册表在预热之后会被锁定;在长生命周期 worker 中运行时尝试注册字体,按设计会被拒绝。

这些安全控制是常量时间的字符串和数组操作,不会增加可测量的单次请求开销。主要的运维开销来自首次使用时的字体解析;请在 worker 启动时预加载字体,以避免首次请求延迟。

本页是该包的威胁模型参考。此处所述的控制措施在源代码中强制执行,并由测试套件加以断言。运维人员必须提供的部署配置已在威胁模型表和时间戳颁发机构步骤中指出。

声明来源条款reference_id 引用标识
PSR-18 客户端发送 PSR-7 请求,返回 PSR-7 响应PSR-18 HTTP Client 规范§2
调用方传入未经转义的结构化日志上下文PSR-3 Logger 规范§1.2

RFC 7469 SPKI 固定是 tsa.pinned_public_keys 配置键采用的形式;该包使用运维人员提供的固定值,本身并不实现该 RFC。

PAdES B-B 签名和时间戳颁发机构集成需要 nextpdf/premium。这是一项可选的 Enterprise 能力;此处所述的 Core 包无需更改代码即可采用它。参见 https://nextpdf.dev/get-license/?intent=laravel-signing

  • /integrations/laravel/configuration/ — 每个 TSA、签名与队列设置键
  • /integrations/laravel/production-usage/ — DI 与错误处理模式
  • /integrations/laravel/troubleshooting/ — 为何路径检查会拒绝输入
  • /integrations/laravel/boot-and-discovery/ — 长生命周期 worker 中的绑定生命周期