產業資訊

資安知識25 分鐘閱讀

檔案上傳怎麼做才安全?副檔名、MIME、惡意程式掃描、隔離儲存與下載完整指南

檔案上傳不能只檢查副檔名。完整說明 MIME、Magic Bytes、深度解析、檔名與路徑、ZIP Bomb、惡意程式掃描、Quarantine、物件儲存、Signed URL 與下載安全。

檔案上傳把使用者可控制的二進位內容帶進應用、解析器、物件儲存與其他使用者的瀏覽器,是一條跨越多個信任邊界的資料供應鏈。安全設計不能只在前端限制副檔名,也不能把防毒掃描當成唯一答案;它需要先定義業務允許的格式,再以大小、名稱、媒體類型、檔案簽章、完整解析與內容檢查建立多層判定,將未通過檔案隔離,最後透過具授權的下載處理器安全交付。本文整理一套可以落地、監控與複驗的完整流程。

乾淨圖片檔經過格式、檔頭、惡意程式與隔離檢查後進入私有物件儲存,危險程式與壓縮檔在獨立路徑被阻擋
01

先講結論:安全上傳是一條受控管線,不是一個副檔名判斷式

使用者選擇檔案之後,內容可能依序經過瀏覽器、Reverse Proxy、應用伺服器、暫存區、格式解析器、防毒引擎、縮圖或文件預覽器、訊息佇列、物件儲存、CDN 與下載端點。任何一層把未驗證資料當成可信格式、可執行程式、檔案路徑或公開內容,都可能產生另一種弱點。

因此最可靠的模型不是『收進來再看看』,而是預設檔案不可信。接收階段只允許必要的業務格式與大小,產生內部 Object ID,立即寫入隔離區;後續工作在受限環境完成類型一致性、深度解析、惡意程式掃描、Content Disarm & Reconstruction(CDR)或重新編碼。只有全部 Gate 通過,狀態才轉為 APPROVED,並依使用者與物件關係提供存取。

這也代表拒絕上傳不是唯一成功條件。系統要能證明危險檔沒有被公開、沒有被 Web Server 執行、沒有在預覽器中觸發、沒有透過壓縮檔寫出隔離目錄,也沒有留下可被猜測的暫存網址。

  • 先由業務定義必要格式,不接受『所有文件』或『所有圖片』這種模糊範圍
  • 在讀取完整內容前限制 Request、檔案、使用者、租戶與時間窗配額
  • 原始檔名、Content-Type 與副檔名視為未信任 Metadata,需正規化與交叉驗證
  • 未完成檢查的檔案只存在私有 Quarantine,不可被公開 URL、預覽器或下游服務取得
  • 通過後仍以物件層授權、正確 Content-Type、Content-Disposition 與 nosniff 安全交付
02

先畫資料流:上傳、處理、儲存與下載是不同信任邊界

上傳 Endpoint 只負責接收並建立待檢狀態,不應同步完成所有昂貴解析。掃描 Worker、轉檔服務與預覽器是高風險 Parser Boundary,應使用低權限、唯讀輸入、隔離網路、CPU/記憶體/時間限制與可丟棄執行環境;物件儲存則只依不可猜但仍需授權的內部 Key 保存內容。

下載是另一個安全決策。即使上傳者合法,也不代表任何知道 URL 的人都可讀取;即使檔案掃描通過,也不代表應直接以内嵌 HTML、SVG 或 PDF 在主站 Origin 執行。每一個邊界都要有明確輸入、狀態、身份、錯誤處置與稽核事件。

檔案處理資料流與信任邊界
階段主要輸入必要控制不得發生
接收Request、使用者、檔案 Metadata認證、授權、CSRF、大小與配額未驗證內容進入 Webroot
隔離原始位元流與內部 Object ID私有儲存、不可執行、狀態鎖定使用者可直接取得 URL
解析/掃描隔離物件受限 Parser、AV、CDR、資源預算高權限程序或開放出站網路
核准所有 Gate 的一致結果原子狀態切換與版本綁定任一檢查未完成即放行
儲存核准物件與安全 Metadata私有 Bucket、最小權限、加密與生命週期以原始檔名當路徑
下載/預覽使用者與 Object ID物件層授權、安全 Header、隔離 Origin以不可猜 URL 取代授權
03

威脅模型:同一個上傳功能可能影響伺服器、瀏覽器與其他系統

MITRE CWE-434 將問題描述為產品允許危險類型的檔案被自動處理。影響不只 Web Shell:檔案可能在伺服器端被執行、利用圖片/PDF/Office Parser 弱點、透過 HTML 或 SVG 對其他使用者造成 Stored XSS、覆寫檔案、消耗儲存與運算資源,或成為散布 Malware 與釣魚內容的公共 Hosting。

風險取決於系統會如何處理檔案。相同 PDF 在只提供 Attachment Download、以瀏覽器 Inline Preview、交給全文索引、擷取附件、轉圖或送進 AI Pipeline 時,攻擊面完全不同。安全需求必須從下游處理行為反推,而不是只看上傳表單接受什麼。

檔案上傳威脅與主要受害面
威脅成立條件可能影響主要縱深控制
伺服器端執行檔案進入可執行 Webroot 或被 Interpreter 處理RCE、資料外洩、橫向移動Webroot 外、不可執行儲存、類型 Allowlist
Stored XSS/主動內容HTML、SVG、XML 或錯誤 MIME 在主站 Origin 顯示Session/資料被竊、使用者操作遭冒用Attachment、nosniff、隔離 Origin、CSP Sandbox
Parser Exploit伺服器解析惡意圖片、PDF、Office 或媒體Worker 入侵、SSRF、檔案讀取Sandbox、更新 Library、最小權限、禁出站
路徑與覆寫信任檔名或壓縮檔內 Path覆寫設定、靜態資源或其他使用者檔案系統 Key、Path Containment、原子寫入
資源耗盡大檔、批次、巨量像素、ZIP Bomb、巢狀封裝CPU、記憶體、磁碟與 Queue 不可用多層 Size/Ratio/Count/Time Budget
惡意內容散布公開下載或分享功能缺乏治理Malware Hosting、品牌與法規風險AV/CDR、授權、檢舉、下架與稽核
04

第一步不是列黑名單,而是定義業務真的需要哪些格式

OWASP 建議以 Extension Allowlist 為起點,只接受業務必要且系統已準備安全處理的類型。『圖片』仍應明確到 JPEG、PNG 或 WebP;『文件』應區分 PDF、DOCX、XLSX 是否真的需要,以及要提供下載、預覽、索引還是轉檔。每多接受一種格式,就多一套 Parser、相容性與修補責任。

Denylist 很難列完所有危險副檔名、大小寫、替代 Handler、雙重副檔名與平台差異,最多只能作為額外告警。Allowlist 也不是單獨防線:合法的 JPEG、PDF 或 DOCX 仍可能帶惡意內容、超大尺寸、Parser Exploit 或外部連結,因此後續仍要深度驗證。

以業務功能建立檔案類型政策
業務用途可考慮格式必要處理預設不接受
個人頭像JPEG/PNG/WebP解碼、像素限制、重編碼、移除 MetadataSVG、HTML、任意原圖格式
簽署文件PDF深度解析、AV/CDR、Attachment 或隔離預覽可執行檔、巨集文件、壓縮包
履歷附件PDF;確有需求才加 DOCXAV、CDR、保存與下載授權HTML、RTF、含巨集格式
資料匯入明確 CSV/JSON Schema編碼、欄位、列數、公式與大小限制任意試算表或二進位檔
備份/套件特定 Archive 或套件格式隔離解壓、Path/Link/Count/Ratio 限制未定義的巢狀封裝
05

五層類型驗證:每一層都只是證據,必須交叉一致

可靠判定會綜合正規化後副檔名、Client 宣告 Content-Type、Server 偵測 Media Type、檔案簽章與完整 Parser 結果。前兩項完全由使用者控制,適合快速拒絕誤傳,但不能作為安全信任;Magic Bytes 可辨識常見格式,卻可能被 Polyglot 或只在開頭偽造的內容繞過。

最有價值的判定是以預期 Library 完整解碼或解析,再確認結構、頁數、像素、內嵌物件、巨集、外部關聯與資源預算。若不同層不一致,例如 `.jpg`、`image/jpeg`,但 Parser 認出 ZIP/Office Container,應 Fail Closed 並保留最小必要證據。

檔案類型驗證的五層證據
層級資料來源能發現不能單獨證明
副檔名正規化後原始名稱明顯非允許格式、使用者誤傳真正內容與安全性
Upload Content-TypeMultipart Header/Client一般格式錯誤不可偽造的類型
Server Media Detection檔頭與內容偵測 Library宣告與內容不一致完整結構安全
File Signature/Magic Bytes格式特徵常見類型是否符合Polyglot、附加 Payload、Parser 行為
完整解析/重編碼專用 Parser/Decoder結構、限制與可安全產出內容所有未知 Parser 0-day 或 Malware
06

副檔名檢查要先正規化:雙重副檔名只是其中一種繞過

檢查前先解析最後一個檔名元件,統一 Unicode 與大小寫策略,移除或拒絕控制字元、前後空白、尾端句點、替代路徑分隔符與平台保留名稱,再確認只有一個允許的最終 Extension。不同 OS、Web Server、Proxy 與 Framework 對 `file.php.jpg`、`file.jpg.php`、大小寫、Trailing Dot、Null Byte 或編碼字元可能有不同解讀。

不要嘗試靠複雜 Regex 猜完所有危險名稱。最安全做法是原始名稱只保存為已清理的顯示 Metadata,實際儲存 Key 由伺服器產生;類型與儲存副檔名則依 Server 完整解析出的核准格式建立,而不是沿用使用者字串。

檔名正規化後的判定項目
檢查風險建議處置證據
最後 Path Segment`../`、反斜線與絕對路徑不使用任何使用者目錄資訊解析前後名稱
Unicode/大小寫相似字、正規化差異與 Case Bypass固定 Normalize 與 Case PolicyCanonical Name
尾端字元Windows 尾端句點/空白與控制字元拒絕或安全替換拒絕原因代碼
多重副檔名不同元件判斷不同 Extension以唯一核准最終格式重新命名原始與儲存類型對照
平台保留名稱裝置名稱、`.`、`..` 與 Shell 特殊值永不作為實際儲存名稱系統 Object ID
07

Content-Type 由 Client 提供,不能視為可信類型

Multipart Upload 的 `Content-Type` 可由瀏覽器或呼叫者自行設定,攻擊者能把任意內容宣告為 `image/jpeg`。它仍有價值:可以快速拒絕一般誤傳,並與副檔名、Server Detection 與 Parser 結果比對;但任何安全放行都不能只依它。

回應端的 Content-Type 則由服務提供,直接影響瀏覽器如何解讀下載內容。RFC 9110 指出錯誤類型與 MIME Sniffing 可能帶來安全風險;系統應依核准後的實際格式產生單一正確 Content-Type,並以 `X-Content-Type-Options: nosniff` 降低瀏覽器自行改判主動內容的風險。

08

Magic Bytes 與 Polyglot:檔頭符合仍不代表整份檔案安全

許多格式在開頭或固定位置有 Signature,能協助辨識 JPEG、PNG、PDF、ZIP 等內容。這比信任 Header 更可靠,但只檢查前幾個 Byte 仍可能接受『開頭像圖片、後面含另一種格式』的 Polyglot,或接受結構損壞、內嵌主動內容與超大資源消耗的合法容器。

應以專用 Parser 完整走訪結構,拒絕多餘尾端資料、異常 Chunk、重複 Header、超出規格的尺寸與未知關聯;是否拒絕某種合法但未使用的 Feature,取決於業務功能與 Parser 能力。Signature 驗證是必要的快速 Gate,不是 AV 或完整解析的替代品。

09

完整解析與重編碼:不要把未知位元原封不動交給下一層

對圖片,可用受維護的 Decoder 真正解碼像素,再以固定格式、品質、尺寸與 Metadata Policy 重新編碼,產生新的安全衍生檔;對文件,可檢查容器結構、巨集、內嵌物件、外部關聯與頁數,再依需求使用 CDR 移除不必要主動元素。使用者原始檔可保留在受限證據區,實際交付的是核准後產物。

Parser 本身也是攻擊面。它應在隔離 Worker 執行,使用最新安全版本,限制 CPU、記憶體、檔案描述符、處理時間、暫存空間與網路;錯誤時 Fail Closed,不能因 Thumbnail 或 Preview 失敗就回退成直接提供原始內容。

深度解析服務的安全執行條件
控制目的建議基線失敗處置
低權限身分限制 Parser Exploit 後能力無 Shell、無 Secret、唯讀輸入、專用輸出銷毀 Worker
網路隔離阻斷 SSRF、外部 Fetch 與 C2預設無出站,必要目的地 Allowlist中止並告警
資源限制避免大像素、深層物件與惡意壓縮耗盡CPU/RAM/Time/Disk/File Count Budget標記 Resource Limit
格式專用 Parser避免通用工具錯誤猜型只解析核准格式與必要 Feature型別不一致即拒絕
安全輸出切斷原始 Payload 與主動功能新 Object ID、固定編碼、最小 Metadata不發布部分產物
10

檔名與路徑:原始 Filename 只能是顯示資料

RFC 6266 明確提醒,Content-Disposition 的 Filename 應被視為建議值,接收端不可因此寫入未授權位置;實作上同樣適用上傳。伺服器應產生 UUID、ULID 或其他內部 Object ID 作為 Key,Original Filename 經長度、Unicode、控制字元與顯示安全處理後,只存放在資料庫 Metadata。

路徑由可信設定與內部 ID 組合,寫入前做 Canonicalization 並確認仍位於預期根目錄。不要把使用者 ID、Tenant ID、原始名稱或 Query Parameter 直接串成 Filesystem Path;也不要假設 `basename()` 足以處理不同編碼、分隔符、Symlink 與競態。物件儲存同樣要避免讓呼叫者任意指定跨租戶 Prefix 或覆寫既有 Key。

原始名稱與內部儲存識別分工
欄位來源用途安全規則
Original Filename使用者UI 顯示與下載建議名稱清理、限長、不可作 Path 或型別依據
Object ID/KeyServer唯一儲存位置高熵、不可覆寫、綁定 Tenant 與 Owner Metadata
Detected Media TypeServer Parser處理與安全回應只取核准結果,不信任 Client
Content HashServer完整性、去重或調查關聯不是 Malware 安全證明
Display FilenameServer 清理後Content-Disposition同時提供安全 ASCII Fallback 與 UTF-8 filename*
11

大小、數量與配額:越早拒絕,越少資源被消耗

只在應用收到完整檔案後檢查 Size,攻擊者已消耗 Reverse Proxy、頻寬、暫存磁碟與 Worker。限制應存在於 CDN/Gateway、Web Server、Framework Multipart Parser、應用業務規則、Object Storage Policy 與非同步 Queue;各層值要一致,錯誤回應也不能洩漏內部路徑或允許無限重試。

除了單檔大小,還要限制 Request 總量、檔案數、每日使用者/租戶配額、並行 Multipart Upload、未完成 Upload、轉檔輸出與保存天數。圖片看似只有數 MB,解碼成巨大像素矩陣後仍可能耗盡記憶體,因此要另外限制 Width、Height 與總 Pixel Count。

上傳資源限制的多層位置
位置限制項目防止監控
CDN/GatewayRequest Body、速率、連線時間頻寬與入口資源耗盡拒絕量、來源與狀態碼
Web/FrameworkMultipart Parts、Header、Temp FileParser 與暫存磁碟耗盡Temp 使用量與解析錯誤
Application業務單檔、檔數、User/Tenant Quota濫用與成本失控配額使用與異常成長
Processing WorkerCPU、RAM、Timeout、Output SizeParser DoS 與轉檔放大超限原因與格式
Object StoragePrefix、Lifecycle、Multipart Cleanup孤兒物件與長期成本未完成 Upload 與儲存成長
12

壓縮檔與 ZIP Bomb:限制解壓後大小、檔數、比例與路徑

CWE-409 描述高壓縮輸入在解壓後產生巨大資料的 Data Amplification。單純限制 ZIP 本身大小無法阻擋;應在真正寫出前檢查宣告與實際的 Uncompressed Size、Compression Ratio、Entry Count、Nested Archive Depth、單 Entry 大小與總處理時間,並以串流方式持續累計,超限立即中止。

每個 Entry Path 都要正規化,拒絕絕對路徑、`..`、替代分隔符、Symlink、Hardlink、Device File 與任何逃出專用 Extraction Root 的結果。解壓在一次性 Sandbox 執行,輸出只透過安全 API 搬到下一階段;不得直接把 Archive 解到應用目錄、Webroot 或既有資料夾。

壓縮檔安全解壓 Gate
Gate必要限制阻擋風險驗證方式
總解壓大小宣告值與實際串流雙重上限ZIP Bomb/磁碟耗盡超限中止且不保留部分輸出
Compression Ratio每 Entry 與整體比例上限高倍 Data Amplification小輸入大輸出測試
Entry Count/Depth檔數與巢狀 Archive 層數Inode、CPU 與遞迴耗盡大量小檔與巢狀測試
Path ContainmentCanonical Path 必須留在 Extraction RootZip Slip/任意覆寫`../`、絕對路徑與混合分隔符
Link/特殊檔拒絕 Symlink、Hardlink、Device 與 FIFO跳出隔離或特殊裝置操作各 Archive 格式專項案例
執行環境無網路、低權限、限制 CPU/RAM/TimeParser Exploit 與資源耗盡Sandbox Policy 與超時證據
13

圖片上傳:副檔名正確,仍要處理像素、Metadata、SVG 與 Decoder

圖片應以實際 Decoder 完整解碼,限制 Width、Height、總 Pixel Count、Frame Count、色彩資料與輸出大小,再重編碼成業務允許的固定格式。重編碼能移除多餘 Chunk 與部分附加內容,但 Parser 必須在受限環境執行,且不能因解碼失敗就回傳原圖。

EXIF 可能包含 GPS、裝置與拍攝時間,應依隱私需求移除。SVG 是 XML 與主動文件格式,不應因為『看起來是圖片』就與 JPEG 同等處理;若沒有完整 Sanitization 與隔離渲染需求,最簡單安全策略是不接受使用者 SVG,或轉成不含主動內容的 Raster Image。動畫格式也要限制 Frame 與總解碼成本。

圖片上傳的格式專項控制
面向風險控制核准產物
尺寸/像素小檔解碼成巨量記憶體Width、Height、Pixel、Frame 上限符合尺寸的重編碼圖
EXIF/Metadata位置、裝置與個資外洩依用途 Strip 或 Allowlist最小 Metadata
SVGScript、外部資源、Event 與 XML 攻擊面預設拒絕;必要時專用 Sanitizer/Rasterize隔離後 Raster 或嚴格安全 SVG
Decoder惡意結構利用 Library 弱點更新版本、Sandbox、資源限制由受控 Worker 產生
Thumbnail縮圖服務成為額外 Parser Boundary與原檔同等隔離與授權新 Object ID 的衍生檔
14

PDF、Office 與其他複合文件:預覽之前先處理主動內容

PDF、DOCX、XLSX 與 PPTX 可能包含 JavaScript、巨集、內嵌檔案、外部連結、OLE Object、表單、媒體與多層壓縮。只確認 ZIP Container 或 `%PDF` Header,無法判斷這些 Feature 是否符合業務需求。應使用格式專用 Analyzer,建立可允許 Feature 清單,並以 AV、Sandbox 或 CDR 補充。

預覽器通常比下載端點更危險,因為它會主動解析與渲染。可將預覽服務放在無使用者 Session Cookie 的獨立 Origin,以 Sandbox、嚴格 CSP、禁網路與短生命週期 Worker 運行;若預覽失敗,不要回退成在主站 Inline 顯示原始檔。

複合文件處理決策
處理需求建議方式必查 Feature安全失敗
只需保存/下載AV+私有儲存+Attachment類型、大小、Malware、授權拒絕或維持隔離
需要預覽隔離 Renderer 產生 Raster/安全衍生檔Script、External Link、Embedded File、資源不提供原始 Inline Fallback
需要內容索引Sandbox Extractor 輸出純文字Parser、外部關聯、編碼與大小不索引且保留待審狀態
需要可編輯CDR 或受控 SaaS/Editor 邊界Macro、OLE、Formula、外部資料明確告知不支援 Feature
15

惡意程式掃描、Sandbox 與 CDR:三者解決的問題不同

Antivirus 適合辨識已知 Malware、可疑特徵與部分啟發式行為,但可能有偵測延遲、Encrypted Archive、格式支援與 False Positive/False Negative;Sandbox 可觀察文件或程式在受控環境的行為,但成本與時間較高;CDR 則以解析並重建內容移除不必要主動元素,適合 PDF、Office 等可安全重建的格式。

三者都不是萬能。掃描服務要記錄 Engine、Signature/Model Version、時間與結果;失敗、Timeout、引擎不可用或結果不確定時,檔案維持 Quarantine,而不是 Fail Open。若檔案含個資、機密或客戶資料,不得未經授權送往公共掃描服務,Hash 查詢也要評估資訊洩漏。

內容安全控制的適用性
控制主要價值限制建議用法
Antivirus已知 Malware 與部分啟發式偵測未知樣本、加密內容、版本延遲作為必經 Gate,記錄 Engine Version
Sandbox觀察開啟/執行後行為成本、時間、Evasion 與資料敏感性高風險格式或事件升級
CDR移除巨集、Script、Embedded Object 等主動 Feature格式支援與功能損失業務可接受安全重建時使用
重新編碼產生最小、可控的圖片/媒體輸出依賴 Decoder 安全在受限 Worker 產生新物件
Hash Reputation快速辨識已知樣本Hash 未知不代表安全,可能洩漏存在性私有資料庫優先、只作補充
16

Quarantine 狀態機:掃描完成前,檔案不能被任何正式流程使用

非同步處理需要明確 State Machine,例如 `UPLOADING → QUARANTINED → SCANNING → APPROVED/REJECTED/ERROR`。狀態存在可信資料庫,Object Version/Hash 與掃描結果綁定;任何內容改變都產生新版本並重新檢查,不能沿用舊檔的 APPROVED 狀態。

這能避免 Time-of-check to Time-of-use(TOCTOU):掃描的是 A,最後發布的卻是同 Key 被覆寫後的 B。掃描 Worker 以唯讀方式取得不可變版本,核准時以條件式或原子操作確認 Hash/Version 未變;下載、預覽、索引、分享與 Webhook 都只接受 APPROVED。

安全檔案狀態機與可用能力
狀態允許操作禁止操作轉移條件
UPLOADING寫入指定暫存 Object Version讀取、分享、解析下游完整性與大小確認
QUARANTINED內部 Scanner 讀取使用者下載、公開 URL、正式索引所有必要 Job 已建立
SCANNING受限 Worker 解析/AV/CDR覆寫同版本、提前發布全部 Gate 得到一致結果
APPROVED依授權下載、預覽與下游處理繞過物件層授權Hash/Version 與結果綁定
REJECTED保留最小調查證據或刪除公開、重試同內容繞過 Policy依 Retention/Incident Policy
ERROR受控重試、人工審查Fail Open引擎恢復或明確風險決策
17

儲存位置:Webroot 外、私有、不可執行,並與應用權限分離

OWASP 的安全優先順序是使用不同主機或服務保存上傳檔,其次是 Webroot 外;若不得不進 Webroot,仍要確保不可執行並透過嚴格存取控制。現代架構通常使用私有 Object Storage Bucket/Container,關閉 Public ACL,以不同 IAM Role 分離 Upload、Scanner、Publisher 與 Download Handler。

『Bucket 是私有的』只是起點。應用 Role 不應擁有整個 Bucket 的任意 List/Read/Delete;Scanner 只讀 Quarantine Prefix、只寫受控結果;Publisher 使用條件式 Copy 或狀態更新;下載服務只在通過物件層授權後取得單一 Approved Object。Server-side Encryption、Versioning、Object Lock 與備份則依資料分類與復原需求設計。

檔案儲存角色與最小權限
角色需要權限不應擁有稽核事件
Upload Service建立專用 Quarantine Object/Multipart讀取他人檔案、發布、刪除歷史Object ID、Owner、Checksum、Size
Scanner/Parser唯讀指定不可變版本、寫結果/衍生檔公開 ACL、修改原檔、任意出站Engine、結果、資源與時間
Publisher將核准版本標記可用或安全搬移跳過結果、任意覆寫Old/New State 與 Version
Download Handler讀取已授權的 Approved ObjectList Bucket、讀 QuarantineUser、Tenant、Object、用途
Lifecycle Job依政策刪除孤兒與到期物件讀取內容或改變 Owner批次範圍、數量、錯誤
18

物件層授權:知道 URL 或 Object ID,不能等同有權下載

下載與刪除必須從已驗證 Session 取得 User/Tenant,再查詢 Object Ownership、角色、分享狀態與業務規則。Object ID 使用高熵值可降低猜測,但不是 Authorization;任何直接以 `/files/{id}`、Storage Key、縮圖 ID 或 Export Job ID 取得內容的端點,都要逐請求驗證。

同一檔案的原圖、縮圖、預覽、轉檔與備份衍生物需繼承一致 Policy。常見錯誤是原檔有授權,Thumbnail CDN、Signed Preview URL 或背景 Export 卻只靠不可猜名稱。跨租戶交換 Object ID、使用已停用帳號、撤銷分享後重播 URL,都應納入負向測試。

19

Direct Upload 與 Presigned URL:它是 Bearer Token,不是掃描通行證

Browser 直接上傳到 Object Storage 可降低應用 Server 負載,但仍要先由應用驗證使用者與配額,產生只能寫入單一隨機 Quarantine Key、限定 Method、短有效期與必要 Header/Policy 的 Signed Request。AWS 明確提醒 Presigned URL 具有 Bearer Token 性質,而且同 Key 已存在時 PUT 可能覆寫;因此 Key 必須唯一且不可重用。

上傳完成後,Client 回報不能直接把狀態改成 APPROVED。Server 應透過可信 Storage Event 或 Head/Metadata 取得實際 Size、Checksum、Version 與 Key,再建立掃描任務。Checksum 可以驗證傳輸完整性,不能證明內容沒有 Malware。Multipart Upload 的每個 Part、Complete 動作、逾期 Session 與未完成 Part 都要受控並定期清理。

Presigned Upload 的安全約束
項目安全設定防止完成後驗證
Object KeyServer 產生單一 Quarantine Key跨租戶寫入與覆寫事件 Key 與預期完全一致
Method/Operation只允許必要 PUT/POST/Multipart 動作權限擴張與任意讀刪實際 Request Method
有效期完成上傳所需的最短窗口URL 洩漏後長期濫用Expired/Replay 測試
Size/Type Policy可簽入 POST Policy 或在入口再次限制過大與明顯錯型實際 Size 與 Server Detection
Checksum簽入並由 Storage 驗證傳輸損壞與內容替換Hash/Version 綁定掃描
FinalizeServer 信任 Storage 狀態而非 Client 宣告跳過上傳或偽造完成Head/Event/Version 與 Scan State
20

下載回應:正確 Content-Type、Content-Disposition 與 nosniff

下載服務依 Server 核准的 Media Type 設定 Content-Type,不沿用 Upload Header。對不需要在瀏覽器中執行的內容使用 `Content-Disposition: attachment`;Filename 只使用清理後顯示值,並依 RFC 6266 同時提供安全 Fallback 與 UTF-8 `filename*`。`X-Content-Type-Options: nosniff` 可降低瀏覽器把內容改判為 Script 或其他主動類型。

對高風險或未知格式可使用 `application/octet-stream` 強制下載,但這不會消除使用者開啟檔案後的 Malware 風險,也不會取代掃描與授權。回應還要考慮 Cache-Control、內容長度、Range、Referrer 與 CDN Key,避免私人檔案被共享快取或在 URL、Log 與 Referer 洩漏 Signed Token。

檔案下載回應安全基線
Header/行為建議主要目的常見錯誤
Content-Type使用 Server 核准的單一實際類型讓 Client 正確、安全解讀沿用使用者宣告或重複 Header
Content-Disposition非必要 Inline 時使用 attachment降低主動內容在 Origin 執行所有上傳內容直接 inline
filename/filename*清理、限長、無 Path,UTF-8 安全編碼避免路徑與 UI 混淆回傳原始未處理 Filename
X-Content-Type-Optionsnosniff降低 MIME Sniffing以為它能取代正確 Content-Type
Cache-Control依資料分類設 private/no-store 或短效策略避免共享 Cache 洩漏私人內容使用 public cache
授權每請求或極短 Signed URL 綁定單一物件阻擋 IDOR/BOLA只靠不可猜 URL
21

Inline Preview:使用獨立 Origin、Sandbox 與最小權限

若業務需要在 Browser 內預覽 PDF、圖片或其他內容,優先顯示由隔離 Renderer 產生的安全衍生物,而不是原始檔。預覽 Origin 不應帶主站 Session Cookie,使用獨立 Hostname、嚴格 CSP、Frame Sandbox、無任意 Script 與受控 CORS;主站透過短效、單物件授權取得內容。

即使使用 `sandbox` Iframe,也要依實際功能審查允許項目;同時給予 `allow-scripts` 與 `allow-same-origin` 可能削弱隔離。預覽服務不得存取內網、Cloud Metadata 或主站 Secret,因為惡意文件可能觸發外部資源與 Parser Request。

22

認證、CSRF、Rate Limit 與批次上傳:入口控制不能被旁路

只有已授權角色能上傳到指定資產或租戶。使用 Cookie Session 的 Endpoint 要有 CSRF 防護;API Token 要限制 Scope;所有入口包含 REST、GraphQL、Mobile API、Admin Import、Editor Paste、Email Ingestion 與 Direct Upload Finalize 都要套用同一 Policy。前端 `accept` 屬性只改善使用體驗,不能取代 Server Validation。

Rate Limit 與配額應同時按 User、Tenant、IP、Object Count、Byte 與處理成本計算。Batch Upload 不能只檢查第一個檔案;部分成功策略、回滾、單檔狀態與總配額要明確。重試要使用 Idempotency Key 或不可變 Upload ID,避免同一內容重複建立與掃描。

上傳入口與必要控制
入口身分/意圖內容控制常見旁路
Web FormSession+CSRF+角色完整 Server Pipeline只靠 accept/JavaScript
REST/GraphQLToken Scope+Object Authorization每個 Part/Variable 檢查替代 Endpoint 未套 Middleware
Direct Storage短效 Presigned RequestQuarantine Key/Policy/FinalizeClient 直接宣告 APPROVED
Admin Import高權限重驗+操作範圍相同或更嚴格 Parser Policy因為是 Admin 就完全信任
Email/Webhook Ingestion可信來源與 Tenant Mapping附件照常隔離與掃描繞過一般 Upload Service
Batch/Archive總配額+逐項授權每項類型與解壓 Gate只檢查外層或第一個檔
23

Metadata、隱私與加密:不要讓安全處理製造新的資料外洩

檔案本體、Original Filename、EXIF、Author、Document Property、AV 結果、OCR 文字、Preview 與 Search Index 都可能包含個資或機密。資料分類應決定誰可讀、是否允許外部掃描、保存多久、是否可跨區域處理、是否需要 Customer-managed Key,以及 Log 能保留哪些欄位。

Encryption at Rest 與 TLS 是必要控制,但不會修復錯誤授權。Key Policy 要與 Storage Role 分離,備份、Replica、縮圖與暫存同樣納入。掃描服務只取得必要期間的存取權,不在一般 Job Log 印出完整 Key、Signed URL、Filename、內容片段或 Token。

24

保留、刪除與孤兒清理:拒絕的檔案也有生命週期

QUARANTINED、REJECTED、ERROR、未完成 Multipart、失敗衍生檔與舊 Version 若沒有 Lifecycle,會長期累積敏感與惡意內容。政策應定義每種狀態保存期限、調查 Hold、刪除方式、Backup/Replica 延遲與誰能覆核;一般營運人員不應能隨意下載拒絕樣本。

使用者刪除帳號、撤回分享、權限變更與法規請求時,要同步處理原檔、衍生物、索引、快取、Signed URL 能力與事件紀錄。若必須保留稽核證據,保留的是最小 Metadata、Hash 與結果,而不是無期限保存完整危險檔。

檔案狀態生命週期範例
資料類型保存原則刪除觸發仍需保留
未完成 Multipart短時間窗口逾時或 Upload Session 取消Upload ID、Owner、Byte 與原因
QUARANTINED/ERROR足以重試或調查的最短期限Job 結束或期限到Hash、Engine、錯誤與時間
REJECTED依 Incident/法遵需要限權保存無調查需求即刪除最小判定證據
APPROVED依業務與資料分類使用者刪除、合約/政策到期必要 Audit Record
Preview/Thumbnail不得超過原檔生命週期原檔刪除或權限撤銷衍生關係與刪除結果
25

Log 與監控:要能回答誰上傳、掃描什麼、何時被取用

每個 Upload 使用唯一 Correlation ID,串起 Session/Token Subject、Tenant、Object ID、Version、Size、Detected Type、Parser、AV/CDR Version、State Transition、下載與刪除。Log 不保留可重播 Signed URL、完整 Credential、原始檔案內容或不必要的個資。

監控要涵蓋拒絕率突增、同來源大量上傳、Size/Ratio/Parser Timeout、AV Engine 不可用、Quarantine 積壓、APPROVED 前讀取嘗試、跨租戶授權拒絕、公開 ACL 變更、Signed URL 異常使用與儲存成本。告警需能定位 Owner 與立即停止發布。

檔案上傳安全監控訊號
訊號可能問題優先確認自動處置
拒絕/未知類型突增攻擊、誤用或 Client 版本問題來源、Tenant、檔頭與 Release限流並通知 Owner
Parser Timeout/Crash惡意樣本、資源限制或 Library 弱點格式、Hash、Worker Version隔離樣本並銷毀 Worker
Quarantine 積壓Scanner 故障或容量不足Engine Health、Queue Age、容量停止新放行、擴容或降載
APPROVED 前讀取授權或狀態檢查缺失Endpoint、Caller、Object State阻擋並建立 Incident
公開 ACL/Bucket Policy 變更配置 Drift 或誤操作Change Actor 與受影響 Prefix自動回復安全基線
Signed URL 異常外洩、重播或過長有效期Signature Age、來源與 Object撤銷底層 Credential/物件能力
26

怎麼測試檔案上傳?以安全樣本建立正向與負向矩陣

測試前取得書面授權,使用專用帳號、租戶、Storage Prefix 與低流量窗口。以最小安全樣本驗證每層行為:正確格式、錯誤副檔名、偽造 Content-Type、簽章不符、損壞結構、超出尺寸/像素/Entry Count、Path Traversal Entry、SVG 主動內容、跨租戶 Object ID 與過期 Signed URL。不要在正式環境散布真實 Malware 或可執行 Web Shell。

OWASP WSTG 提到可用 EICAR 測試檔驗證 Anti-malware 流程;即使如此,也要先與 SOC、AV Owner、Storage Owner 核准,避免觸發正式事件或被其他系統擴散。測試證據要確認最終狀態、儲存位置、下游未執行、下載被拒絕與 Log/Alert 正確,不只看 API 回傳 400。

檔案上傳安全測試矩陣
測試面安全案例預期結果必要證據
Allowlist未允許與雙重副檔名接收前或 Quarantine 內拒絕Canonical Name 與原因
類型一致性Header/Extension/Signature/Parser 不一致Fail Closed,不建立 APPROVED各層偵測結果
資源過大、巨量像素、ZIP Ratio/Count/Depth早期中止且部分輸出清除限制值與實際值
內容安全核准的 EICAR/測試標記或 CDR 測試文件Engine 偵測、隔離、無下載Engine Version、State、Alert
路徑`../`、絕對路徑、Symlink 與混合分隔符未寫出 Extraction RootSandbox File Inventory
授權跨使用者/租戶 Object、Preview 與衍生檔一致拒絕且無內容洩漏Account/Object Matrix
Signed URL過期、改 Key、改 Method、重播與覆寫簽章或 Policy 拒絕Request 與 Storage Event
下載HTML/SVG/未知內容安全 Attachment/隔離預覽、nosniffResponse Header 與瀏覽器行為
27

安全檔案上傳的 10 階段標準流程

這套流程把內容判定、隔離、儲存與交付拆成可稽核的 Gate。主路徑只讓符合業務格式、資源限制、完整解析與內容安全檢查的不可變版本前進;任何未知、錯誤、Timeout 或引擎不可用都停留在 Quarantine,不以可用性理由自動放行。

安全檔案上傳十階段交付物
階段核心工作必要 Gate交付物
01 身分與範圍認證、CSRF、角色、Tenant 與用途Caller 有權建立該類物件Upload Authorization
02 資源預算Request、File、Count、Quota、Timeout入口層即限制Resource Budget
03 名稱分離清理顯示名稱、Server 產生 Object Key使用者字串不作 PathObject Metadata
04 類型驗證Extension、MIME、Signature、Parser 交叉一致只接受業務 AllowlistType Evidence
05 隔離解析專用 Parser、Sandbox、重編碼/CDR無出站且受資源限制Parsed Artifact
06 內容安全AV、Sandbox、主動內容與格式 Feature未知/Timeout 不放行Scan Evidence
07 Archive GateSize、Ratio、Count、Depth、Path、Link輸出留在隔離根目錄Extraction Manifest
08 Quarantine 決策結果綁定 Hash/Version,原子轉狀態所有必要 Gate 通過Approval Record
09 私有儲存不可執行 Object Store、最小 IAM、Owner MappingPublic ACL 關閉Storage Policy
10 安全交付物件授權、短效能力、正確 Header、Audit/Delete只有 APPROVED 可讀Download/Lifecycle Evidence
從認證上傳、大小配額、檔名分離、類型與檔頭一致、隔離解析、惡意程式與壓縮檔檢查、Quarantine、私有儲存到授權下載與刪除的十階段流程
安全上傳不是單一驗證器,而是從接收、隔離、解析、核准、私有儲存到安全交付的完整狀態流程。
28

修復與複驗:確認危險內容沒有在另一條路徑被使用

修復先定位 Root Cause:是 Allowlist 未落在 Server、檔名直接形成 Path、Quarantine 可公開讀、Scanner Fail Open、同 Key 可覆寫、預覽器直接開原檔,還是下載缺少授權。只新增一條 Extension Regex,無法關閉後段的儲存、解析與交付風險。

複驗要保留原始負向樣本與 Account/Object Matrix,先確認合法檔仍可完整上傳、處理與下載,再確認每個旁路都拒絕;同時查 Storage、Queue、Preview、CDN、Log 與衍生檔,證明沒有部分產物、公開 URL 或背景 Job。若 Library 或 AV Rule 更新,應對既有 Quarantine/Approved 物件依風險重新掃描。

檔案上傳修復後的關閉條件
修復面正向案例負向案例關閉證據
類型核准格式完整處理偽造 Header/Extension/Signature 不一致Parser 結果與 Reject State
路徑/Key新 Object 唯一且可追蹤Traversal、同 Key 覆寫、跨 PrefixStorage Event 與無越界寫入
內容安全乾淨檔轉 APPROVED測試 Malware/主動內容/TimeoutEngine Version 與 Fail Closed
資源邊界內檔案完成Size/Pixel/ZIP/Batch 超限早期拒絕且 Temp 清除
授權Owner 可下載核准物件他人、他租戶、撤銷分享與 Quarantine所有衍生路徑一致拒絕
下載正確格式與檔名HTML/SVG/未知格式與 MIME SniffHeader、隔離 Origin 與瀏覽器結果
29

常見誤區:看起來有檢查,不代表形成安全閉環

上傳安全最常失敗在各元件解讀不一致:前端看 Extension、API 看 Content-Type、Storage 信任 Original Filename、Web Server 依另一個 Extension 執行、Browser 又對回應做 MIME Sniff。真正安全的流程必須由 Server 產生單一核准類型與狀態,並讓所有下游只接受同一份可信結果。

檔案上傳常見誤區與正確作法
誤區為什麼不夠正確作法
前端 `accept` 已限制Request 可被直接修改Server Allowlist 與全流程驗證
只檢查副檔名可偽造、雙重名稱、平台解讀不同正規化+類型交叉一致+完整解析
Content-Type 是 image/jpeg完全由 Client 宣告Server Detection、Signature 與 Decoder
Magic Bytes 符合就安全可能是 Polyglot、異常結構或惡意合法格式完整解析、重編碼、AV/CDR
防毒沒報就安全未知樣本、解析器與主動內容仍存在多層 Gate、Sandbox 與最小 Feature
UUID 很難猜,不用授權不可猜不等於合法存取每請求 Object/Tenant Authorization
私有 Bucket 就完成應用 Role、Signed URL、Preview 與衍生檔仍可能外洩最小 IAM、短效能力與全路徑稽核
掃描失敗先放行把防護不可用轉成攻擊旁路維持 Quarantine、受控重試與告警
30

上線前檢查清單

以下項目應綁定實際 Endpoint、Storage、Parser、角色與證據。每個未完成項目都要有 Owner、補償控制、到期日與複驗條件;不要用『已有防毒』概括檔案類型、路徑、資源、授權與下載風險。

  • 每個上傳用途都有明確格式、大小、數量、像素、頁數、Archive 與 Feature Allowlist
  • 所有入口包含 Web、API、Mobile、Admin、Email、Webhook 與 Direct Upload 共用同一政策
  • 認證、CSRF、角色、Tenant、Rate Limit、Quota 與 Idempotency 已在 Server 驗證
  • Extension、Client Content-Type、Server Detection、Magic Bytes 與完整 Parser 結果必須一致
  • 原始 Filename 只作已清理顯示 Metadata;實際 Object Key 由 Server 產生且不可覆寫
  • 圖片、PDF、Office、SVG、Archive 與其他核准格式都有專用 Parser、Feature 與資源限制
  • AV、Sandbox、CDR 或重編碼依格式使用;Engine Error/Timeout 時維持 Quarantine
  • Archive 限制 Uncompressed Size、Ratio、Entry Count、Depth、Path、Symlink 與處理時間
  • Quarantine 私有、不可執行,APPROVED 前不可下載、預覽、索引、分享或進入下游
  • Storage Role 採最小權限,Public ACL 關閉,原檔、衍生物與各 Version 權限一致
  • Signed Upload/Download 限單一 Object、Method、短效時間並視為需保護的 Bearer Token
  • 下載使用核准 Content-Type、清理後 Content-Disposition、nosniff 與資料分類 Cache Policy
  • Log、監控、Retention、刪除、Multipart Cleanup、Incident 與修復複驗流程均已演練
31

Redmark 如何協助檢查檔案上傳與公開攻擊面

Redmark 協助團隊將網站資產、登入後流程、掃描任務、風險證據與修復追蹤集中管理。規劃檔案上傳檢測時,團隊應先確認合法授權、允許格式、專用測試帳號與不含真實個資的安全樣本,再依實際 Endpoint、角色、租戶、儲存與下載路徑建立測試矩陣。

自動化掃描能發現公開 Endpoint、類型與回應配置、常見旁路與部分內容處理風險,但 Parser Sandbox、Private Storage IAM、AV/CDR Pipeline 與 Retention 仍需要系統 Owner 提供架構證據。最佳做法是把外部掃描、內部配置查核與原始負向案例的修復複驗整合成同一個持續流程。

REF

資料來源

Redmark Editorial
START WITH ONE WEBSITE

從第一個網站開始,建立持續弱掃流程

以更低的執行成本持續檢測網站弱點,更早發現問題,也讓每一次修復都有清楚的方向。

免費開始掃描