IIS Intergrated & Classic Mode
2022-04-16
筆記 IIS 應用程式集區 Managed Pipeline Mode 設定為 Intergrated Mode (整合模式) 與 Classic Mode (傳統模式) 的差別,並延伸討論 ISAPI Filter 與 ISAPI Extension 對應到 Http Module 與 Http Handler 的關聯,並且示範如何實作 Http Module 用以處理常見的資安弱點掃描實務 🙂
Managed Pipeline Mode
首先參考 mmx 在 stackoverflow 所分享關於 What is the difference between 'classic' and 'integrated' pipeline mode in IIS7? 的回答,用以區別 CLassic 以及 Intergrated Mode。
Classic Mode
IIS 直接地藉由 ISAPI extensions 以及 ISAPI filters 來處理 Requests。ASP.NET 也被視為 ISAPI extention 的一員,其所使用 aspnet_isapi.dll 與其他的 ISAPI extention 地位並無不同 (如 StaticFile 或者 PHP)。
在 Classic Mode IIS 根據情況選擇對應的 ISAPI extension 來處理 Requests。
Integrated Mode
在 Integrated mode,從 IIS 7 開始 IIS 與 ASP.NET 整合成單一的管線,所有的 Requests 都可以被 ASP.NET 所處理,至此 ASP.NET 在 Http Requests 的處理上融入 IIS 成為一體,ASP.NET Http Modules 如同 Classic Mode 的 ISAPI filters 而 Http Handler 則如同 ISAPI extensions。
此外以往的 ISAPI filters 以及 ISAPI extensions 是使用 C & C++ 來開發,在 Intergrated Mode 下則可以使用 .NET 使用 C# 或者 VB 來開發。
更多關於 Intergrated Mode 的介紹可以參考 Scott Hanselman Moving old apps from IIS6 to IIS8 and why Classic Mode exists 以及 ASP.NET Integration with IIS 7。
更多關於 ISAPI 的介紹可以參考 Wikipedia 或者是 Vito 在 自訂 HTTP Modules and HTTP Handlers 以及 網頁生命週期 所做的說明 😃
Custom Http Module With Integrated Mode
關於黑大在 徹底移除 IIS Response Header 版本資訊 使用 StripHeaders Modules 的方式徹底移除 IIS StaticFile 的 Response Header 資訊。除了黑大介紹的安裝模組方式外,也可以利用 Integrated Mode 的統一 Pipeline 原理,直接客製 Http Moodule 來處理:
參考 如何理解IIS 7的兩種應用程序池的管道模式(Managed Pipeline Mode) 是在 Response Content 加入內容,調整為移除 IIS 暴露出來的伺服器資源。
首先在專案中加入 Http Module,檔案的位置可以放在 App Code 當中。
App Code/RemoveServerInfo.cs
namespace CodeShops
{
public class RemoveServerInfo : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreSendRequestContent +=
new EventHandler(PreSendRequest);
}
public void PreSendRequest(Object source, EventArgs e)
{
var response = HttpContext.Current.Response;
response.Headers.Remove("X-AspNet-Version");
response.Headers.Remove("X-AspNetMvc-Version");
response.Headers.Remove("Server");
}
public void Dispose()
{
}
}
}
接著到 web.config
中進行 Http Module 的註冊。
web.config
<system.webServer>
<modules>
<add name="removeServerInfo" type="CodeShops.RemoveServerInfo"/>
</modules>
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</system.webServer>
部署到 IIS 後即可以驗證成果,在應用程式集區使用 Intergrated Mode 的情況下,對所有的檔案,包含靜態檔案在內,不論是 MvcHandler 或者 StaticFile Handler 所回應的檔案都不會包含機敏的伺服器資訊 😄
反之如果將應用程式集區調整為 Classic Mode,伺服器的敏感資訊就會暴露出來 😫
runAllManagedModulesForAllRequests
網路上有些朋友討論到主動在 web.config 加入 runAllManagedModulesForAllRequests 會啟用所有的 Requests 都進入 Managed Modules 的處理。
但值得思考的是這點的差異與 Integrated Mode 有什麼差異,不是原本就應該如此嗎?所有的 Requests 都會受到 Managed Modules 處理?
為什麼需要特別啟用 runAllManagedModulesForAllRequests,又為什麼大家都說啟用會影響效能呢 🤔
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
以上的疑惑,從保哥在 ASP.NET MVC 4 在 .NET 4.0 與 .NET 4.5 的專案範本差異 的分享,有了更深的認識。
保哥提到在 .NET 4.0 與 .NET 4.5 的其中一項差異在於 runAllManagedModulesForAllRequests 預設值的差別,在 .NET 4.0 為 True 在 .NET 4.5 則改為 False。而這個設定具體的影響如下:
- runAllManagedModulesForAllRequests True
- All Http Requests,包含已註冊的、未註冊的副檔名對應,都會進入開發人員自行客製 Http Module
- runAllManagedModulesForAllRequests False
- Http Requests 中屬於已註冊的副檔名對應,會進入開發人員自行客製 Http Module
這個說明與本文上述實驗的 Custom Http Module With Integrated Mode 相對照,在 runAllManagedModulesForAllRequests 為 False 的情況下,自己 Custom 的 Http Module 有影響 .aspx, .html, .css, .js 等檔案,因此這些副檔名檔案都是屬於 已註冊的副檔名。
現在可以確定已註冊的副檔名 (.aspx, .html, .css, .js 等) 對應,在 Managed Pipeline Mode 是 Intergrated 下,都會進入開發人員自行客製的 Http Module,但是否同樣會進入其他 Managed Http Module?
另外又興起一個疑問如何判斷副檔名的註冊與否?從保哥的案例可以知道 .php 肯定是屬於未註冊的副檔名,而註冊的方法是透過在 web.config 中加入 Handler,需要加入的包括
- (PHP) ExtensionlessUrlHandler-ISAPI-4.0_32bit
- (PHP) ExtensionlessUrlHandler-ISAPI-4.0_64bit
- (PHP) ExtensionlessUrlHandler-Integrated-4.0
有趣的是加入後所使用的 type 和 MVC Routing 所使用的同樣都是 ExtensionlessUrlHandler,或許是因為這個示範操作不是真正要處理 .php 只是要轉將 .php 交給 MVCHandler 進行處理的緣故。
<system.webServer>
<handlers>
<add name="ExtensionlessUrlHandler-Integrated-4.0"
path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
<add name="(PHP) ExtensionlessUrlHandler-Integrated-4.0"
path="*.php"
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
HTTP Modules
接著來一探 Http Module,深入在上一個部分還留下的好奇。首先示範如何確認 ASP.NET Application 當中所有的 HTTP Modules (包含 Native 以及 Managed Module)?
ModuleController.cs
public ActionResult Index()
{
var modules = HttpContext.ApplicationInstance.Modules;
Tuple<string, string, string>[] data =
modules.AllKeys
.Select(
x => new Tuple<string, string, string>(
x.StartsWith("__Dynamic") ? x.Split('_', ',')[3] : x,
modules[x].GetType().Name,
modules[x].GetType().AssemblyQualifiedName
))
.OrderBy(x => x.Item1)
.ToArray();
return View(data);
}
Views/Moduls/Index.cshtml
@model Tuple<string, string, string>[]
@{
ViewBag.Title = "Index";
}
<div style="margin-top:20px">
<table class="table table-bordered table-striped" style="font-size: 12px">
<thead>
<tr>
<th style="width:20%" class="bg-info">Name</th>
<th style="width:20%" class="bg-info">Type</th>
<th style="width:60%" class="bg-info">Assembly</th>
</tr>
</thead>
<tbody>
@foreach (Tuple<string, string, string> x in Model)
{
<tr>
<td style="width:20%">@x.Item1</td>
<td style="width:20%">@x.Item2</td>
<td style="width:60%">@x.Item3</td>
</tr>
}
</tbody>
</table>
</div>
分別在 IIS 上,切換集區的 Managed Pipeline Mode 列出使用的 Http Module:
從顯示的結果觀察,會發現雙方有不少重疊的 Http Module,並無明顯區別 Managed Module 以及 Native Module。
而只出現在 Classic Mode 的 Http Module:
- ErrorHandlerModule
- ErrorHandlerModule => System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- PassportAuthentication
- PassportAuthenticationModule => System.Web.Security.PassportAuthenticationModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- ServiceModel
- HttpModule => System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
只出現在 Intergrated Mode 的 Http Module:
- UrlMappingsModule
- UrlMappingsModule => System.Web.UrlMappingsModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- removeServerInfo
- RemoveServerInfo=> CodeShops.RemoveServerInfo, CodeShops, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
其中 removeServerInfo 是在本文的 Custom Http Module With Integrated Mode 部分所加入的 Custom Http Module,只會出現在 Intergrated Mode。
從 runAllManagedModulesForAllRequests 實驗後,延伸又在進行一個實驗,分別使用 Intergrated / Classic Mode 搭配 runAllManagedModulesForAllRequests True / False 的組合,驗證 Http Custom Module 是否有發揮作用。
結論是 Intergrated Mode 為 True,Http Custom Module 才有發揮作用,不論 runAllManagedModulesForAllRequests True 或者是 False;反之 Classic Mode 則絕不會發揮作用,即使是 .aspx, .asp, .ashx 等由 ASP.NET Handler 所負責的 Requests。
這點和列出所有的 Http Module 所得到的結果一致,因此推論由 Application 所列出的 Http Module 對於 .aspx, .asp, .ashx, .html, .css 以及 .js 等所有具備 Handler 對應的檔案都會作用,至於為對應 Handler 的項目,會被 IIS 報錯提示應該為其註冊 Hadnler 或者提供 MIME 以檔案下載的方式服務。
最後的好奇是如何驗證 .html, .js 等靜態檔案在 Intergrated Mode 下除了 Custom Http Module 有被處理到外,是否有被其他 Managed Http Module 所處理到呢?
目前的猜想是肯定的,Http Module 的使用與否和 Application 所列出的有關與 runAllManagedModulesForAllRequests True / False 無關。
驗證的方式可以從 Managed Module 的功能來確認,這一點和 到底 runAllManagedModulesForAllRequests 在做什麼? 就等著更多的知識與工具來驗證了 😎
IIS Shows Http Modules
除了在 ASP.NET Application 列出 Http Module 外,同樣也可以在 IIS Server 上對站台檢視 Http Module。
發現在 Classic Mode 只會顯示 Native 的 Http Module,在 Intergrated Mode 則會顯示完整的 Http Module。但實際使用 ASP.NET Application 列出時,又沒有這麼多的數量上的差別,所以在 IIS Server 檢視的 Http Module 與 ASP.NET Application 的不同就仍有待研究了。
IIS Config
IIS Server (inetsrv) Folder Path
cd C:\Windows\System32\inetsrv\config\applicationHost.config
.NET Framework Folder Path
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\