把下載視作完整交易

顧客按下載發票、預約確認書或服務紀錄。商店可能已核實顧客有權看該頁,但獨立的檔案端點仍可能送錯內容、使用危險的檔名,或讓瀏覽器以意料之外的方式解讀回應。可用的設計應從伺服器上的唯一檔案識別碼、目前的存取資格核對及清楚的回應規格開始。要決定顧客可以取得甚麼、承諾哪種格式、儲存後的檔名,以及檔案暫時不可用時如何處理。頁面有一條可按連結,並不能證明以上結果正確。

權限核對應在每次檔案請求時執行,不能只在顯示下載連結的頁面做一次。連結可能被複製、登出後重訪,或從另一帳戶打開。伺服器應把不透明識別碼對應至檔案記錄,確認目前請求者有權取得,再串流核准的位元組。不要把儲存路徑直接暴露,也不要以可猜到的檔名作為授權規則。OWASP 的檔案指引建議透過應用程式處理器,把識別碼對應到儲存檔案,尤其適用於需要控制存取的檔案。授權失效時,應回傳設計好的錯誤結果,並避免洩露文件內容。

如實標示檔案類型

Content-Type 標頭應描述實際送出的檔案。PDF 不應標成 HTML,也不應單憑不可信的上載檔名猜測未知二進制檔案的類型。MDN 說明,媒體類型告訴瀏覽器收到的是甚麼;缺少可靠標示時,瀏覽器可能自行從位元組推測。設定 X-Content-Type-Options: nosniff 可以要求瀏覽器尊重所宣告的類型,但前提仍是類型本身正確。nosniff 不會修好錯誤類型,也不會令惡意檔案變得安全。若商店接受顧客上載的檔案,必須先進行輸入驗證及適當處理,再讓其他人下載。

要分清系統生成的發票與顧客上載的附件。發票產生器應知道自己輸出甚麼格式,確實產生 PDF 位元組時才可標示相應 PDF 媒體類型。附件則需要列明允許格式及驗證流程;上載時由客戶端宣告的類型並非可靠證據。OWASP 警告檔案類型資料可以偽造,並建議驗證內容、限制格式及把檔案放在不會直接執行的網頁位置。這些是上游控制。下載回應不能把不可信格式偽裝成無害類型,或讓它在商店來源下當作可執行內容。

明確選擇下載或頁內顯示

Content-Disposition 決定瀏覽器應在頁內顯示,還是把回應視為附件。若顧客預期儲存文件,attachment 是清晰的選擇,並應由伺服器提供安全檔名。MDN 指出 filename 可影響建議的儲存名稱,而 filename* 可在同時提供 ASCII 後備名稱時,支援編碼的非 ASCII 名稱。兩者都應從可信記錄建立,移除路徑分隔符及控制字元,副檔名亦須與已驗證類型一致。不可把不可信的上載檔名原封不動放進 HTTP 標頭。

若檔案確實是經驗證的安全 PDF,頁內預覽可能符合產品需要,但會改變顧客預期及瀏覽器接觸檔案的方式。應問顧客是否需要儲存副本、頁面有否可用的替代方式,以及新分頁開啟私人檔案後,共用裝置會否留下可見內容。連結的 HTML download 屬性不能取代伺服器回應;MDN 記載它與 Content-Disposition 的互動,例如回應中的檔名可影響建議儲存名稱。應在支援的瀏覽器檢查實際效果,而非只看模板裏的一個屬性。

令快取政策符合私隱承諾

私人發票或服務紀錄不應放進可能回傳給另一位使用者的共用快取。MDN 定義 Cache-Control: private 為容許瀏覽器私人快取,但排除共用快取;no-store 則要求快取不要儲存回應。兩者的取捨取決於文件敏感度,以及商店對共用裝置的假設。高度敏感的一次性匯出可能需要 no-store;其他需登入的文件則可經風險評估後使用不同政策。規則應直接套用到檔案回應,包括可能顯示個人資料的錯誤及跳轉路徑,不能假設 HTML 頁面的設定會自動傳到檔案。

瀏覽器把檔案儲存到裝置後,本機副本已超出商店控制。任何 HTTP 快取指令都不能收回已下載的檔案。文件含有個人資料時,應以清楚文字提醒顧客,不要聲稱商店可遙距刪除既有副本。若商店使用限時簽署網址,須檢視有效期、適用範圍及可能外洩的路徑;但最終檔案回應仍需正確的類型、下載處理方式及快取政策。簽署網址只是暫時授予存取的方法,本身並非完整的下載規格,也不能取代判斷收件人是否應取得該連結。

以顧客情境逐步核對

假設顧客進入訂單頁下載 PDF 收據。頁面連結使用不透明文件識別碼,而不是儲存檔名。收到請求時,服務核對登入帳戶與該訂單的關係,取出已核准的 PDF 位元組,回傳正確 PDF Content-Type、nosniff、附有安全收據檔名的 attachment 處理方式,以及選定的私人快取政策。顧客得到的檔案名稱與內容,應和畫面所示訂單相符。這是一個設計示例,不代表任何現有商店已實作上述安排。

接着每次只改一個條件。若顧客登出,舊連結應帶到未授權或登入結果,而不是回傳 PDF 位元組。若另一帳戶打開複製的連結,服務不能洩露原顧客的收據。若文件產生器失敗,應說明檔案暫不可用,提供稍後重試或支援路徑;不能把 HTML 錯誤頁命名為 receipt.pdf。若儲存的位元組與預期格式不符,就應停止交付及調查,而非加上誤導的 PDF 標籤。每一種情況都能成為工程及客服共同使用的驗收結果。

檢查實際 HTTP 往返

測試應從顧客介面開始,查看網絡回應,而不只看瀏覽器下載清單中的檔名。記錄狀態碼、跳轉後最終網址、Content-Type、X-Content-Type-Options、Content-Disposition、Cache-Control,以及在安全測試環境核實的檔案開首位元組或格式特徵。確認檔案能由預期閱讀器開啟,內容屬於正確記錄。分別以有效登入、過期登入及另一顧客帳戶重試;另用含中文與空格的檔名,查看支援瀏覽器如何顯示 ASCII 後備名稱和編碼名稱。

錯誤處理也應像成功下載般仔細測試。網絡失敗後,訂單頁仍要可理解,顧客可重試而不用猜測檔案是否已存好。遭拒的請求,錯誤內容或建議檔名都不應洩露另一顧客姓名。部分或損壞檔案不能被呈現成已完成收據。客服需要可追查訂單及下載嘗試的參考資料,但日誌不應儲存文件本身或秘密存取權杖。當預期顧客能取得正確檔案,而所有受測未授權路徑均沒有回傳私人位元組,才算完成發佈驗收。

在顧客按下之前說清楚

下載控制項應以日常用語說明文件及格式,例如「下載收據(PDF)」,而不是只有「下載」。若檔案需要即時產生,應告訴顧客正在準備及何時可取。如果文件對應某張訂單或某段期間,應在檔案外的頁面列出脈絡,讓顧客選對。伺服器尚未能提供已核准位元組前,不能自信地顯示「已準備好」。前端承諾須與實際回應規格一致,包括格式、建議檔名及存取結果。

可請客服同事不用開發工具完成下載,再由工程人員檢查同一請求的網絡回應。客服應能辨認文件、找到已存檔案,並理解登入過期提示;工程人員則應核對回應標頭,確認被拒路徑沒有回傳文件位元組。兩種視角缺一不可:技術上正確的附件仍可能難以尋找,而漂亮的下載按鈕亦可能掩蓋送錯或外洩的檔案。正式接受發佈前,應把兩邊觀察到的結果對齊。若兩者描述的檔案格式、權限或失敗結果不同,就要在上線前修正,並再次從顧客頁面走完整個下載流程。

進行前先確認

  • 每次檔案請求會否核對權限?
  • 類型、下載方式及檔名是否符合實際位元組?
  • 私人快取及故障路徑是否符合文件敏感度?

資料來源及延伸閱讀