安全最佳實踐
本指南提供在正式環境中部署 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 供應商
| 供應商 | 網址 | 備註 |
|---|---|---|
| DigiCert | https://timestamp.digicert.com | 廣受信任,AATL 成員 |
| Sectigo | https://timestamp.sectigo.com | 全球可用性佳 |
| GlobalSign | https://timestamp.globalsign.com | 歐洲供應商 |
| FreeTSA | https://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 合規以維持長期有效性
延伸閱讀
- 安全性總覽 — 安全架構與設計哲學
- 威脅模型 — 詳細威脅分析
- PAdES B-LTA — 簽章等級與實作
- ETSI 密碼學要求 — 演算法要求