引擎威脅模型
重點摘要
標題為「重點摘要」的區段本頁是 NextPDF 核心引擎的威脅模型。它列舉引擎在處理受攻擊者影響的輸入(HTML、CSS、SVG、字型、影像,以及既有 PDF)時,視為範圍內的攻擊類別;說明每一項外部資源能力的預設姿態;並指出用於緩解各類別的程式內防護。
邊界。 威脅模型記錄已考量的威脅,以及針對這些威脅採取的緩解措施。它並不主張不存在漏洞。此處未列出的類別,並不因此就被證明不存在——它可能落在模型目前範圍之外。尚未實作的功能在發布前必須通過正式的威脅審查把關。請把本頁視為一份刻意設計的紀錄,而非安全性證明。
composer require nextpdf/core:^3此處描述的防護屬於核心套件的一部分;不需要額外相依套件即可啟用,且預設就會啟用。
概念總覽
標題為「概念總覽」的區段本模型遵循 OWASP 威脅塑模流程(owasp_threat_modeling#x1.x11.p6)建議的結構:將系統拆解為不受信任輸入跨越信任邊界的各個點、列舉每個邊界上的威脅,並記錄對應緩解措施。
引擎的主要信任邊界是 文件擷取(document ingestion):凡是由他處撰寫的內容進入引擎之處——一份遠端樣式表、一個 @font-face 來源、一個 <image href>、一份內嵌的 XML 發票、一份待檢視的 PDF——都可能指示引擎抓取、剖析或解壓縮某個東西。其治理原則是 預設拒絕(deny-by-default):每一項外部資源能力,在呼叫端透過政策物件明確啟用之前都是關閉的。這是將 NIST SP 800-53 Rev. 5 CM-7(nist_sp_800_53r5#x4.x182.p14)的最小功能性基準套用到算繪引擎上:最嚴格的立場,就是建構子的預設值。開啟某項能力,是呼叫端明確做出的決定。
API 介面
標題為「API 介面」的區段威脅模型本身不是 API。用來表達此模型的政策物件記載於各模組頁面;與信任相關的進入點是外部資源政策契約(ExternalResourcePolicyInterface,以 DefaultExternalResourcePolicy 作為全部拒絕的預設值),以及 URL 與 XML 防護(UrlValidator、XmlGuard)。本頁參照它們的行為,不重新記載其簽章。
程式碼範例——快速上手
標題為「程式碼範例——快速上手」的區段安全姿態就是預設值;不需要撰寫任何程式碼即可取得:
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Html\DefaultExternalResourcePolicy;
// Out of the box: @font-face blocked, @import blocked, background-image// blocked, SVG external refs blocked. A document that tries to fetch a// remote resource gets a system-font fallback or an ignored rule — not an// outbound request.$policy = new DefaultExternalResourcePolicy();程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段開啟某項能力必須是刻意且範圍狹窄的決定。若正式環境的呼叫端必須允許透過 HTTPS 載入 CDN 託管的 webfont,就要明確選擇啟用並限定其範圍:
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Html\DefaultExternalResourcePolicy;
// Explicit, scoped opt-in. The HTTPS scheme is required; size and glyph// caps still apply; the URL still passes the SSRF guard before any fetch.$policy = (new DefaultExternalResourcePolicy()) ->withFontFaceAllowed(['https']);邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 未實作不等於碰巧安全。 諸如 CSS
background-image url()這類能力並未實作,因此目前攻擊面為零。但文件中明確載明,它們在未來任何實作之前,都必須先通過正式的威脅 gate(把關)。程式碼不存在,是現階段的緩解措施,而非永久保證。 - DNS 重新繫結是個不斷變動的目標。
UrlValidator會 resolve(解析)主機名稱並回傳解析後的 IP,讓呼叫端可以釘選該連線(CURLOPT_RESOLVE),藉此關閉驗證與抓取之間的 TOCTOU 時間窗。這是盡力而為的防禦,而非絕對防禦。若維運人員位在寬鬆的出口 proxy 後方,仍可能觸及這套函式庫看不見的內部主機。 - 權限位元並不是一種存取控制。 一份「封鎖複製」的文件仰賴閱讀器配合,而非強制執行。這一點在安全模型一節中說明;之所以在此特別點出,是因為它是常見的威脅模型誤解。
這些防護的設計目標,是快速失敗並限制工作量:XML 防護在剖析前就拒絕 DOCTYPE 並限制輸入大小;影像路徑在解壓縮前就強制執行百萬像素與位元組上限;URL 防護在開啟任何 socket 前就依 scheme/host 加以拒絕。安全預設值的代價是一個被拒絕的請求,而非一個緩慢的請求。
安全性注意事項
標題為「安全性注意事項」的區段所考量的攻擊類別及其程式內緩解措施如下:
| 威脅類別(CWE / OWASP) | 在 PDF 引擎中的攻擊向量 | 程式內防護 |
|---|---|---|
SSRF 伺服器端請求偽造(OWASP Top 10 2025;owasp_top10_2025#x3.x1.p26) | @font-face/@import/url() 指向 169.254.169.254 或內部主機;TSA/OCSP/CRL 抓取器 | UrlValidator::validateExternalUrl() 封鎖 private/reserved/loopback/link-local 範圍與雲端中繼資料端點、拒絕危險 scheme,並在解析 DNS 後回傳 IP 以供連線釘選 |
XXE(cwe_top25_2025#x28.x2.p42) | 內嵌 XML 發票或 XMP 封包中的外部實體 / DOCTYPE | XmlGuard::loadXml() 強制執行 LIBXML_NONET,並直接拒絕任何 DOCTYPE 宣告;同時擋下 XML 1.0 禁用的控制字元,且設有輸入大小上限 |
| 解壓縮炸彈 | 用 1×1 影像掩飾一份 100 MP 的酬載;過大的 WOFF2 | 影像路徑在解壓縮前即強制執行百萬像素上限與位元組上限;字型路徑則限制檔案大小與字元數 |
| 路徑穿越 | file:///etc/passwd,經由字型或 SVG 的 src | 外部資源預設全部拒絕;本機檔案路徑在明確啟用時,會透過 realpath() 依目錄允許清單加以解析 |
| 內容注入 | 精心構造的字串掙脫某個 PDF 運算子;data:/javascript: href | 輸出時會跳脫 PDF 字串;對註解則採 scheme 允許清單並清理 href |
這些預設值彙整為一個全部拒絕的外部資源姿態:字型、@import、background-image 與 SVG 外部參照,在呼叫端逐一依 scheme 選擇啟用之前都是關閉的;這與隨程式碼一同維護的安全性質涵蓋矩陣一致。
本頁記錄的是 已考量 的威脅。它不是滲透測試報告,也不主張所列的緩解措施已臻完整,或不存在其他類別的弱點。
符合性
標題為「符合性」的區段這並非符合性設定檔。本威脅模型參考 OWASP 威脅塑模流程與 CWE Top 25 弱點分類法(cwe_top25_2025#x28.x2.p42);它並不主張符合任何安全認證方案。獨立評估屬於稽核範疇,而非本文件範疇。