iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Software Development

一套真實運作中的 Laravel 系統,拆解它的原生機制系列 第 22

Day 22:檔案上傳安全——一次真實資安事件後,才補上的 webshell 防護

  • 分享至 

  • xImage
  •  

前言:「先能上傳圖片就好」,是很多專案的起點

「這個功能就是讓使用者上傳一張輪播圖、附加一份新聞附件,能用就好,先別想太多。」

這句話聽起來合理——上傳功能的核心需求確實單純:使用者選一個檔案、系統存起來、之後能被讀取。但「先能用」跟「先能用,但沒有限制檔案類型」中間,藏著一個容易被忽略的安全漏洞。今天要講的不是一篇「檔案上傳安全懶人包」,而是一個真實案例:一個上傳功能上線之後,真的被拿來上傳過惡意檔案,事後才補上防護。

今日目標

  • 理解「檔案上傳」功能天生帶著什麼樣的攻擊面
  • 看一個真實案例:防護是先出事、後補上的,不是設計完善才上線
  • 學會兩種基本防護手法:副檔名白名單、MIME type 檢查
  • 建立一個誠實的認知:資安防護常常是事後才補上的,這不丟臉,重點是有沒有補

本文主體

攻擊面:一個檔案上傳欄位,其實是一扇門

一個檔案上傳功能,如果沒有限制,使用者能上傳的不只是圖片跟文件——理論上任何格式的檔案都能塞進去。如果伺服器剛好會執行某些副檔名(例如 PHP),而上傳目錄剛好在網站可以直接存取的路徑下,攻擊者上傳一個偽裝成圖片、實際上是可執行程式碼的檔案,再直接用網址存取它,等於在伺服器上開了一個後門——這種攻擊手法有個專門的名字:webshell。

這不是理論上的假設。這個系列的素材專案,就真的發生過一次這樣的事件。

一次真實的資安事件

在某次 commit 紀錄裡,可以看到這樣的訊息:

fix: 限制檔案上傳副檔名,防止 webshell 上傳攻擊
fix: 限制 News 檔案附件上傳類型,阻擋 PHP 等可執行檔

再往前追一支相關 commit,會看到一個目錄被加進 .gitignore——一個專門存放事後鑑識分析紀錄的目錄。這代表什麼?代表這不是維護者「想到可能有這個風險,先修一下」的防禦性重構,而是真的有人上傳過惡意檔案,事後做了鑑識分析,才回頭修這個防護

誠實講這件事,是因為這正是多數上線中系統的真實樣貌:不是每個系統一開始就把所有安全防護想清楚,很多防護是踩過真的事件之後才補上的。誠實面對這件事,比假裝「我們一開始就設計得很完善」更有參考價值——因為多數讀者的專案,可能也還沒補上這一塊。

防護怎麼補:副檔名白名單 + MIME type 檢查

補上的防護分兩層:

第一層:限制副檔名。 不管使用者的檔案叫什麼名字,只接受白名單內的副檔名(例如圖片格式、常見文件格式),任何看起來像可執行檔的副檔名(.php.phtml.php5 這類)一律拒絕。

第二層:驗證 MIME type,不只信任副檔名。 副檔名只是檔名的一部分,可以被輕易偽造——把一個 PHP 檔案改名成 image.jpg,副檔名檢查就會被騙過去。真正可靠的做法是讀取檔案的實際內容特徵(Laravel 的檔案驗證規則本身就有基於檔案內容判斷 MIME type 的機制),確認檔案的「實際身份」跟它宣稱的副檔名一致。

用一組對照來看差異:

❌ 只信任使用者上傳時宣稱的副檔名
$request->file('attachment')->getClientOriginalExtension();
→ 使用者(或攻擊者)完全可以控制這個值,
  上傳一個實際是 PHP 程式碼、但檔名叫 photo.jpg 的檔案,
  這個檢查完全擋不住

✅ 用 Laravel 驗證規則限制副檔名 + 檢查實際內容特徵
'attachment' => 'file|mimes:jpg,jpeg,png,pdf,docx|max:10240',
→ mimes 規則不只看副檔名,會進一步比對檔案的
  實際內容特徵是不是真的符合宣稱的格式,
  副檔名被偽造也擋得住

為什麼「先能用」很容易漏掉這一步

檔案上傳功能在開發初期,測試的人通常是自己人——上傳一張真的圖片,看看有沒有正常顯示,功能驗收就過了。這個驗收流程完全不會觸發「上傳一個偽裝成圖片的惡意檔案會怎樣」這個情境,因為沒有人會在功能驗收階段刻意去攻擊自己的系統。

這正是為什麼這類防護常常是「事後補的」:功能本身能正常運作,跟「這個功能對外部惡意輸入夠不夠健壯」,是兩件完全不同的事,而只有後者需要用攻擊者的視角去想。

今日思考題

回想你維護的專案裡,有沒有一個上傳功能,你只驗證過「正常情況下能不能用」,但沒有驗證過「如果有人刻意上傳一個惡意檔案會怎樣」?現在花五分鐘檢查一下:你的上傳驗證規則,是只看副檔名,還是也驗證了實際內容特徵?

今日重點回顧

  • 檔案上傳功能天生帶著攻擊面:只驗證「正常情況下能不能用」不夠,還要驗證「面對惡意輸入夠不夠健壯」
  • 這個系列的素材專案,真的發生過一次上傳攻擊事件,防護是事後補上的,不是一開始就設計完善
  • 兩層防護:副檔名白名單(先擋掉明顯不對的類型)+ 驗證實際內容特徵(副檔名可以被偽造,不能只信任它)
  • 誠實面對「防護是事後補的」這件事,比假裝一開始就想清楚更有參考價值

明日預告

明天要看另一個「先出事、後補防護」的真實案例——這次不是應用層的資安問題,是 Docker 反向代理環境下,一個中介層設定錯誤,導致系統一直拿不到使用者真實 IP 的坑。


上一篇
Day 21:定期爬取外部網頁——Generator 分頁 + PSR-18 discovery + retry plugin 組合技
系列文
一套真實運作中的 Laravel 系統,拆解它的原生機制22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言