前情提要:突如其來的測試環境危機
最近台電因為白帽滲透測試的關係,需要暫時關閉測試機。但開發團隊偶爾還是需要測試環境來驗證功能,於是我們決定把程式架在其他環境上。原本想說簡單設個 IIS IP 白名單就能解決,沒想到踩到了一個大坑...
問題出現:IP 白名單完全失效
當我興高采烈地在 IIS 管理器中設定「IP 位址及網域限制」,把信任的 IP 加入白名單後,發現一個詭異的現象:
- 設定為「允許所有」→ 正常運作 ✅
- 設定為「拒絕未指定,只允許白名單」→ 所有人都 403 錯誤 ❌
檢查 IIS Log 後發現真相大白:
1 | c-ip: 192.168.0.28 # 所有請求都顯示這個 IP! |
原來所有外部請求都是透過 192.168.0.28 這台 Proxy Relay 伺服器轉發過來的。IIS 只看得到代理伺服器的 IP,完全拿不到真實的客戶端 IP。
深入追查:尋找真實 IP 的蹤跡
建立一個簡單的測試頁面來檢查所有可能的 HTTP 標頭:
1 | <%@ Page Language="C#" %> |
測試結果發現關鍵線索:
1 | REMOTE_ADDR: 192.168.0.28 |
太好了!代理伺服器有正確傳送真實 IP,只是格式是 IP:PORT。
解決方案一:URL Rewrite 改寫 REMOTE_ADDR
首先確認 IIS 已安裝 URL Rewrite 模組,然後在 web.config 中加入:
1 | <system.webServer> |
重要: 需要在 IIS 的「URL 重寫」功能中先新增允許修改的伺服器變數:
- 點選「URL 重寫」
- 右側「檢視伺服器變數」
- 新增
REMOTE_ADDR
設定完成後測試:
1 | REMOTE_ADDR: 125.228.130.66 # 成功改寫! |
意外發現:IIS IP 限制的執行時機陷阱
興奮地以為問題解決了,結果發現 IIS 的「IP 位址及網域限制」還是不行!
深入研究後發現:IIS 的 IP 限制功能執行在請求處理管道的早期階段,在 URL Rewrite 之前。所以它仍然看到的是原始的 c-ip,而不是改寫後的 REMOTE_ADDR。
這解釋了為什麼 IIS Log 中:
c-ip仍然是192.168.0.28X-Forwarded-For正確顯示真實 IP- URL Rewrite 成功但 IP 限制失效
解決方案二:程式碼層面的 IP 控制
既然 IIS 內建功能不給力,我們就自己來!在 Global.asax.cs 中實作:
1 | protected void Application_BeginRequest(object sender, EventArgs e) |
最佳實務:環境分離的設定管理
為了避免正式機和測試機搞混,在 web.config 中加入可設定的開關:
1 | <appSettings> |
這樣就能:
- 測試機:
EnableIPRestriction = true - 正式機:
EnableIPRestriction = false
加碼:IIS Log 顯示真實 IP
為了方便追蹤,可以讓 IIS Log 也顯示真實 IP:
- IIS 管理器 → 網站 → 「記錄」
- 「選取欄位」→ 「自訂欄位」
- 新增:
- 欄位名稱:
X-Forwarded-For - 來源:
Request Header - 來源名稱:
X-Forwarded-For
- 欄位名稱:
之後 Log 就會顯示:
1 | c-ip: 192.168.0.28 X-Forwarded-For: 125.228.130.66:14185 |
踩坑總結
這次解決問題的過程學到幾個重點:
網路架構很重要
有代理伺服器的環境下,要搞清楚真實 IP 的傳遞方式。
IIS 功能的執行時機
不是所有 IIS 功能都能配合 URL Rewrite,要了解請求處理管道的順序。
設定檔化設計
多環境部署時,要設計成可設定的方式,避免程式碼硬編碼。
善用除錯工具
建立簡單的測試頁面可以快速定位問題,比盲目調整設定有效多了。
後記
原本以為是個簡單的 IP 白名單設定,結果挖出了代理伺服器、IIS 請求管道、URL Rewrite 等一堆技術細節。雖然過程有點曲折,但最終找到了一個既實用又彈性的解決方案。
