疑難排解:加密與權限旗標
範圍與邊界
標題為「範圍與邊界」的區段這些條目涵蓋引擎透過 NextPDF\Exception\EncryptionException 與 NextPDF\Security\Exception\DecryptionFailedException 拋出的解密失敗情境,以及 PDF 權限旗標的邊界。
先釐清邊界,能避免最常見的誤解:PDF 加密字典中的權限旗標只記錄作者意圖;本函式庫不會將它們當作強制執行的存取控制機制。忽略這些旗標的閱讀器(reader)仍可列印、複製或修改內容。請把這些旗標視為對配合的閱讀器提出的請求,而非強制手段。
條目:加密作業失敗
標題為「條目:加密作業失敗」的區段- 症狀。 拋出
EncryptionException,訊息形式為Encryption operation "<op>" failed using algorithm "<algorithm>"。 - 可能原因。 密碼運算無法執行,通常是缺少 OpenSSL 擴充或其設定錯誤、金鑰素材無效,或在加密邊界處 IV 長度不正確。
- 證據/診斷。
getContext()會回傳algorithm與operation。operation的值是encrypt、decrypt或key_derivation之一,可用來判斷失敗發生在哪個階段。 - 解法。
- 確認已安裝並載入 OpenSSL PHP 擴充。
- 讀取
operation欄位以定位失敗的階段。 - 若為
key_derivation,請驗證密碼或金鑰輸入。 - 重新執行該呼叫。
- 相關。 例外參考。
條目:解密因結構性原因而失敗
標題為「條目:解密因結構性原因而失敗」的區段- 症狀。 拋出
DecryptionFailedException,訊息形式為Decryption failed for "<algorithm>": <reason>。 - 可能原因。 密文因非竄改性原因而無法處理,例如密文被截斷、缺少 IV,或在 API 邊界處提供了錯誤的金鑰。完整性檢查並未執行,因為可供評估的素材不足。
- 證據/診斷。
getContext()會回傳algorithm與reason。原始碼將DecryptionFailedException記載為有別於TamperedDataException:這個例外代表設定或傳輸錯誤,並非竄改徵兆。不要單憑它就視為安全事件。 - 解法。
- 讀取
reason以辨識結構性缺陷,例如ciphertext shorter than IV+tag。 - 確認密文在傳輸時未遭截斷。
- 確認在邊界處提供的金鑰,正是文件加密時所用的金鑰。
- 重新執行該呼叫。
- 讀取
- 相關。 簽章與時間戳失敗。
條目:拋出「資料遭竄改」例外
標題為「條目:拋出「資料遭竄改」例外」的區段- 症狀。 拋出
NextPDF\Security\Exception\TamperedDataException,而非DecryptionFailedException。 - 可能原因。 完整性檢查已執行且未通過。這有別於結構性的解密失敗:當時有足夠的素材可評估完整性,但完整性不成立。
- 證據/診斷。 原始碼對比這兩個類別:
DecryptionFailedException屬於結構性問題,並非安全事件;TamperedDataException則表示內容已進入驗證流程但未通過驗證。 - 解法。
- 將輸入視為不可信;不要使用解密後的內容。
- 從可信來源重新取得該文件。
- 若改用已知良好的來源後仍持續失敗,請擷取
getContext()以供事件報告之用。
- 相關。 簽章與時間戳失敗。
條目:權限旗標未能阻止下游動作
標題為「條目:權限旗標未能阻止下游動作」的區段- 症狀。 產生的文件已設定權限旗標(例如禁止複製或列印),但閱讀器仍複製或列印內容。
- 可能原因。 這屬於預期內的邊界,而非缺陷。本函式庫不會將傳入核心加密字典建構器的權限整數當作強制手段套用。這些旗標屬於建議性中繼資料;不遵守它們的閱讀器並不會受到 NextPDF 阻擋。
- 證據/診斷。
src/Security/Encryption/EncryptionDictionaryBuilder.php宣告了buildDict(int $permissions, string $fileId),其參數記載為ignored; retained for forward compatibility。該方法一開始即執行unset($permissions, $fileId)。透過src/Inspect/PdfPermissions.php揭露的權限旗標是唯讀的檢查欄位,而非強制執行層。 - 解法。
- 不要依賴權限旗標來阻止列印、複製或修改。 產生器函式庫無法強制執行這些旗標。
- 在必須限制存取的情境下,請控管檔案本身的散布,或在 PDF 之外套用存取控制系統。
- 使用
PdfPermissions時,只用於回報既有文件所宣告的旗標,而非用來宣稱那些動作已被阻止。
- 相關。 簽章與時間戳失敗。
條目:PDF/A 下加密遭拒
標題為「條目:PDF/A 下加密遭拒」的區段- 症狀。 當同一次建構同時啟用 PDF/A 並要求加密時,拋出
NextPDF\Security\Exception\IncompatiblePdfAModeException。 - 可能原因。 PDF/A 設定檔禁止在尾段(trailer)中使用
Encrypt鍵。無論以何種呼叫順序,引擎都會拒絕這個組合。 - 證據/診斷。 請參閱 PDF/A 與 PDF/UA 條目 所引用的條款、
getContext()欄位,以及失敗路徑測試。 - 解法。
- 決定這份文件需要的是封存一致性,還是加密。
- 移除你不需要的那項屬性對應的呼叫。
- 重新執行該管線。
- 相關。 PDF/A 與 PDF/UA 驗證。
邊界案例與陷阱
標題為「邊界案例與陷阱」的區段DecryptionFailedException與TamperedDataException代表不同意義:前者是結構性失敗,後者是完整性驗證失敗。請依類別進行分支判斷,而非依訊息文字。- 核心加密字典建構器會忽略權限整數;若有實作依賴核心套件提供權限強制執行,就是建立在誤解之上。
PdfPermissions只有在完整檢查深度下,才會針對已加密文件被填入,且只反映該文件宣告的旗標。欄位有值並不代表該動作已被阻止。