Skip to content

安全最佳实践

本指南提供在正式环境中部署 TCPDF-Next 的可行安全建议。遵循这些实践可确保您的数字签名、加密与文件生成符合企业级安全标准。

证书管理

获取证书

在正式环境中使用数字签名时,请使用受信任证书授权机构(CA)所签发的证书:

证书类型使用场景典型有效期费用
文档签署(个人)个人签名1-3 年$50-200/年
文档签署(组织)企业全域签名1-3 年$200-500/年
合格证书(eIDAS)欧盟法律效力1-3 年$100-400/年
Adobe AATL 证书在 Adobe Acrobat 中默认信任1-3 年$300-1000/年

TIP

若您的签名需要在 Adobe Acrobat 中无需手动配置信任即可被认可,请使用 Adobe 核准信任列表(AATL) 上的 CA 所签发的证书。

证书轮替

php
use YeeeFang\TcpdfNext\Certificate\CertificateStore;

$store = new CertificateStore();

// 从安全目录加载证书
$store->loadFromDirectory('/etc/tcpdf-next/certs/', '*.pem');

// 自动选取有效且未过期的证书
$activeCert = $store->getActiveCertificate('document-signing');

if ($activeCert->getExpirationDate() < new \DateTimeImmutable('+30 days')) {
    // 触发证书续期警报
    $logger->warning('签署证书将在 30 天内到期', [
        'subject' => $activeCert->getSubject(),
        'expires' => $activeCert->getExpirationDate()->format('Y-m-d'),
    ]);
}

建议事项:

  • 至少在到期前 30 天进行证书续期
  • 通过自动化警报监控证书到期时间
  • 立即吊销已遭泄露的证书
  • 维护所有证书操作的记录

私钥存储

存储方式层级(从最安全到最不安全)

方式安全等级使用场景
硬件安全模块(HSM)最高企业级、受监管行业
云端 KMS(AWS KMS、Azure Key Vault、GCP KMS)云原生部署
具强密码短语的 PKCS#12 文件小型部署
PEM 文件(加密)中低开发、测试
PEM 文件(未加密)最低正式环境中绝不使用

使用 HSM(建议方式)

php
use YeeeFang\TcpdfNext\Signature\Pkcs11Signer;

// 通过 PKCS#11 使用硬件安全模块
$signer = new Pkcs11Signer(
    modulePath: '/usr/lib/softhsm/libsofthsm2.so',
    slotId: 0,
    pin: getenv('HSM_PIN'), // PIN 码从环境变量取得
    keyLabel: 'signing-key-2026'
);

$pdfSigner = new PdfSigner($pdf);
$pdfSigner->setExternalSigner($signer)
    ->setLevel(SignatureLevel::PAdES_B_LTA)
    ->sign();

使用云端 KMS

php
use YeeeFang\TcpdfNext\Signature\CloudKmsSigner;

// AWS KMS 示例
$signer = CloudKmsSigner::awsKms(
    keyId: 'arn:aws:kms:eu-west-1:123456789:key/abcd-1234',
    region: 'eu-west-1',
    algorithm: 'RSASSA_PSS_SHA_256'
);

// Azure Key Vault 示例
$signer = CloudKmsSigner::azureKeyVault(
    vaultUrl: 'https://my-vault.vault.azure.net',
    keyName: 'signing-key',
    keyVersion: 'abc123',
    algorithm: 'RS256'
);

// Google Cloud KMS 示例
$signer = CloudKmsSigner::gcpKms(
    keyName: 'projects/my-project/locations/europe-west1/keyRings/signing/cryptoKeys/doc-signing/cryptoKeyVersions/1',
    algorithm: 'RSA_SIGN_PSS_2048_SHA256'
);

PKCS#12 文件搭配密码短语

php
// 从文件加载 PKCS#12,密码短语从环境变量取得
$p12Content = file_get_contents('/etc/tcpdf-next/keys/signing.p12');
$passphrase = getenv('SIGNING_KEY_PASSPHRASE');

$certs = [];
openssl_pkcs12_read($p12Content, $certs, $passphrase);

$signer = new PdfSigner($pdf);
$signer->setCertificate($certs['cert'], $certs['pkey'])
    ->setLevel(SignatureLevel::PAdES_B_LTA)
    ->sign();

// 从内存清除机密数据
sodium_memzero($passphrase);
sodium_memzero($certs['pkey']);

DANGER

切勿将私钥存储于:

  • 源代码仓库中
  • 在进程清单中可见的环境变量
  • 共享文件系统上的未加密文件
  • 未加密的数据库字段
  • 日志文件或错误消息中

时间戳服务器选择

选择 TSA 的标准

标准需求
RFC 3161 合规必要
TLS(HTTPS)必要
响应时间< 2 秒
可用性99.9%+ SLA
哈希算法SHA-256、SHA-384、SHA-512
证书有效期10 年以上
审计跟踪提供
地理位置考量数据驻留要求

建议的 TSA 供应商

供应商网址备注
DigiCerthttps://timestamp.digicert.com广受信任,AATL 成员
Sectigohttps://timestamp.sectigo.com全球可用性佳
GlobalSignhttps://timestamp.globalsign.com欧洲供应商
FreeTSAhttps://freetsa.org/tsr免费,适合测试用途

WARNING

免费 TSA 服务可能无法提供正式环境所需的可靠性、SLA 或审计跟踪。对于具法律效力的签名,请使用商用 TSA 供应商。

TSA 容错备援

配置多个 TSA 服务器以实现高可用性:

php
use YeeeFang\TcpdfNext\Timestamp\TsaPool;

$tsaPool = TsaPool::create()
    ->addServer('https://timestamp.digicert.com', priority: 1)
    ->addServer('https://timestamp.sectigo.com', priority: 2)
    ->addServer('https://timestamp.globalsign.com', priority: 3)
    ->setFailoverStrategy('priority'); // 或 'round-robin'

$signer->setTimestampClient($tsaPool);

签名验证

验证外部来源的 PDF

接受来自外部的已签署 PDF 时,务必验证签名:

php
use YeeeFang\TcpdfNext\Signature\SignatureValidator;
use YeeeFang\TcpdfNext\Certificate\CertificateStore;

$trustStore = new CertificateStore();
$trustStore->loadFromDirectory('/etc/tcpdf-next/trusted-roots/');

$validator = new SignatureValidator($trustStore);
$result = $validator->validate('/path/to/received.pdf');

if (!$result->isValid()) {
    foreach ($result->getErrors() as $error) {
        $logger->error('签名验证失败', [
            'error' => $error->getMessage(),
            'code' => $error->getCode(),
        ]);
    }
    throw new \RuntimeException('无效的签名');
}

验证检查清单

确保您的验证流程检查以下项目:

  • [ ] 签名完整性(CMS 签名验证通过)
  • [ ] 证书链(签署者到受信任根证书)
  • [ ] 证书有效性(未过期、未吊销)
  • [ ] 密钥用途(已设置 digitalSignature 位)
  • [ ] 字节范围覆盖(整份文件已签署)
  • [ ] 无未经授权的签署后修改
  • [ ] 时间戳有效性(如有)
  • [ ] 算法合规性(无 SHA-1、无弱密钥)

安全部署

环境配置

bash
# .env(Laravel 示例)

# 签署证书(PKCS#12)
TCPDF_SIGNING_CERT_PATH=/etc/tcpdf-next/certs/signing.p12
TCPDF_SIGNING_CERT_PASSPHRASE=  # 通过密钥管理器配置,勿写在 .env

# 时间戳服务器
TCPDF_TSA_URL=https://timestamp.digicert.com
TCPDF_TSA_HASH_ALGORITHM=SHA-256

# 安全策略
TCPDF_MIN_RSA_KEY_SIZE=2048
TCPDF_REJECT_SHA1=true
TCPDF_STRICT_PARSING=true

# 资源限制
TCPDF_MAX_MEMORY=256M
TCPDF_MAX_EXECUTION_TIME=120
TCPDF_MAX_PDF_SIZE=104857600

文件系统权限

bash
# 证书目录:仅允许 Web 服务器用户读取
chown -R www-data:www-data /etc/tcpdf-next/certs/
chmod 700 /etc/tcpdf-next/certs/
chmod 600 /etc/tcpdf-next/certs/*.p12
chmod 600 /etc/tcpdf-next/certs/*.pem

# 输出目录:仅允许 Web 服务器用户写入
chown -R www-data:www-data /var/lib/tcpdf-next/output/
chmod 700 /var/lib/tcpdf-next/output/

# 临时目录:可写入,非全局可读
chown -R www-data:www-data /tmp/tcpdf-next/
chmod 700 /tmp/tcpdf-next/

内容安全

从用户提供的内容(HTML、图片)生成 PDF 时:

php
use YeeeFang\TcpdfNext\Security\ResourcePolicy;
use YeeeFang\TcpdfNext\Security\NetworkPolicy;

// 严格限制资源加载
$resourcePolicy = ResourcePolicy::strict()
    ->allowLocalDirectory('/app/public/assets/')
    ->denyAllRemote(); // 禁止所有外部资源获取

// 若确实需要远程资源
$networkPolicy = NetworkPolicy::create()
    ->denyPrivateNetworks()
    ->denyLoopback()
    ->denyLinkLocal()
    ->allowDomain('cdn.yourcompany.com')
    ->setMaxRedirects(3)
    ->setRequestTimeout(10);

$pdf = PdfDocument::create()
    ->setResourcePolicy($resourcePolicy)
    ->setNetworkPolicy($networkPolicy)
    ->build();

速率限制

对于公开的 PDF 生成 API,实施速率限制:

php
// Laravel 中间件示例
Route::post('/api/generate-pdf', [PdfController::class, 'generate'])
    ->middleware('throttle:pdf-generation');

// 在 RouteServiceProvider 中
RateLimiter::for('pdf-generation', function (Request $request) {
    return Limit::perMinute(10)->by($request->user()->id);
});

合规检查清单

财务文件

  • [ ] 使用 PAdES B-LTA 签名确保长期有效性
  • [ ] 使用 AATL 列表上的证书取得 Adobe 信任
  • [ ] 使用具 SLA 的商用 TSA
  • [ ] 嵌入所有验证数据(OCSP + CRL)
  • [ ] 保留签署审计日志直至监管要求期限
  • [ ] 使用 PDF/A-4 满足归档需求

医疗保健(HIPAA)

  • [ ] 以 AES-256 加密含 PHI 的 PDF
  • [ ] 使用证书式(公钥)加密实现多收件者访问
  • [ ] 记录所有签署密钥的访问行为
  • [ ] 将签署密钥存储于 HSM 或云端 KMS
  • [ ] 为所有已签署文件实施审计跟踪

欧盟 eIDAS 合规

  • [ ] 使用 eIDAS 合格 TSP 签发的合格证书
  • [ ] 使用 eIDAS 合格 TSA 的合格时间戳
  • [ ] 实现 PAdES B-LTA 以取得合格电子签名
  • [ ] 确保 PDF/A-4 合规以维持长期有效性

延伸阅读

以 LGPL-3.0-or-later 许可证发布。