# 從安全角度理解 Pillow 的 `open()`、`verify()`、`load()` 圖像上傳不只是「接收圖檔」,而是將外部輸入送入解碼器(解析器)進行處理。 因此 Pillow 的三個方法,重點不在功能說明,而在於**何時呼叫**(即何時開啟攻擊面)。 --- ## `open()` 不是「把像素拉起來」的函式 {#sec-2e015f3fe462} `Image.open()` 是**延遲**執行。它只會開啟檔案並辨識格式,**像素資料可能尚未讀取**,且檔案句柄可能保持開啟。 在安全/運營上,正確使用 `open()` 的方式很簡單: * 先用 `open()` 取得格式、寬高等**輕量資訊** * 依政策拒絕:允許格式、最大解析度/像素數、上傳容量限制 * 再進行驗證/解碼 簡言之,`open()` 應作為「在解碼前提取可判斷資訊」的工具。 --- ## `verify()` 保障什麼,保障什麼不是 {#sec-bb315850bac9} Pillow 的 `verify()` 會嘗試確認檔案是否損毀,但**不會實際解碼圖像資料**。若發現問題會拋出例外,且在 `verify()` 後若要繼續使用圖像,必須重新開啟檔案。 安全觀點的結論有兩點: * **優點**:避免重度解碼,快速過濾「損毀檔案」 * **限制**:`verify()` 只表示「目前未顯著損毀」;問題仍可能在 `load()` 時才暴露。 --- ## `load()` 在驗證階段隨意呼叫會危險 {#sec-1eb3249bd446} `load()` 會真正執行**解碼(包含解壓縮)**並將像素載入記憶體。此時即成為 DoS(資源耗盡)攻擊面。即使檔案外觀小,解碼後可能膨脹至巨量。 Pillow 會以例外或警告處理**「解壓縮炸彈」**風險,並設有預設閾值(如 128Mpx)作為保護。 Django 亦因同樣原因,在圖像上傳驗證時使用 `verify()` 而非 `load()`。程式碼中有「`load()` 會將整張圖載入記憶體,成為 DoS 向量」的註解,實際上會在 `Image.open()` 後呼叫 `verify()`。 --- ## Django/DRF 使用時:`ImageField` 內部已呼叫 `verify()`,再呼叫可能重複 {#sec-c03f143819d2} Django 的表單 `ImageField` 驗證會在內部執行 `Image.open()` + `verify()`,DRF 的 `serializers.ImageField` 亦同樣委派給 Django 進行驗證。 因此若已使用 DRF 的 `serializers.ImageField`: * 在 `validate()` 中再次呼叫 `verify()` 以「檢查損毀」通常是**重複工作**。 * 若需自訂商業驗證或額外安全檢查,建議改用 `FileField`(自行設計驗證流程),以明確掌握成本與責任。 --- ## 為使用者上傳的檔案確保「安全」的實務做法 {#sec-bafb5e21c1fb} ![上傳檔案安全處理流程圖](/media/editor_temp/6/489648ac-eb83-4132-808b-f3ce0e07c366.png) 最實際的答案是: **「不要直接使用上傳原始檔,讓伺服器解碼後再以新檔案儲存,並只使用這個新檔案」**。 1. 用 `open()` 讀取寬高/格式等**低成本資訊**,並以政策做一次性拒絕。 2. 用 `verify()` 過濾「明顯損毀」的檔案。 3. 在受限環境下解碼,並將像素標準化為 RGB/RGBA 等通用格式。 4. 伺服器以選定格式重新編碼,產生新檔。 5. 服務僅儲存/提供伺服器重新編碼的檔案。 此策略的優點在於「伺服器掌控最終輸出」,能大幅移除原始檔中不必要的元資料或奇怪結構。 當然,重新編碼仍包含 `load()` 的解碼工作,故需先設定**像素數/記憶體限制**(防止解壓縮炸彈),並盡量在工作者/隔離流程中執行,以確保安全。 --- ## 總結 {#sec-7d348ef3294d} * `open()`:**辨識 + 低成本資訊確認**(像素可能尚未載入) * `verify()`:**損毀檔案的初步過濾**(不解碼,使用前需重新開啟) * `load()`:**解碼/記憶體使用開始**(驗證階段應避免頻繁呼叫) * 上傳安全實務:**只信任伺服器重新編碼的結果**(但必須先設置限制/隔離) --- **相關文章**: * [開發者眼中的圖像檔案結構:拆解圖像檔案](/whitedec/2026/1/14/developer-view-image-file-common-structure/) * [Django 圖像上傳安全指引:高效處理避免伺服器崩潰](/whitedec/2026/1/13/django-image-upload-security-guide/) * [python-magic:以檔案內容取代副檔名的實用方法](/whitedec/2026/1/15/python-magic-file-type-detection/)