【茶包射手日記】認識 web.config compilation 與 httpRuntime 的 targetFramework
| | | 2 | |
處理一枚 TLS 1.2 茶包,藉此釐清了一些 .NET Framework 版本觀念,筆記備忘。
故事是有個 ASP.NET MVC / .NET Framework 4.5.2 網站,WebClient 連某個 HTTPS 網址時出現典型的 基礎連接已關閉: 接收時發生未預期的錯誤 / 客戶端與伺服器無法溝通,因為它們沒有公用的演算法 (The client and server cannot communicate, because they do not possess a common algorithm) 錯誤。很明顯是網站只支援 TLS 1.2,而 .NET 4.6+ 才預設使用 TLS 1.2 連線,解法有三種:
- 升級到 .NET Framework 4.6 以上版本 參考
- 在程式碼加上 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
- 設定 SystemDefaultTlsVersions、SchUseStrongCrypto Registry 參考
我選擇第一種做法,將 ASP.NET MVC 網站升級到 4.8,重跑程式,卻照樣彈出「沒有公用的演算法」錯誤... 登楞! 莫非「.NET 4.6+ 預設改用 TLS 1.2」 的說法有誤,我還錯信了八年?
開了個全新 .NET 4.5.2 Console Application 測試以下程式,成功重現 TLS 連線錯誤;將版本改成 4.8 重新編譯執行都可成功,印證先前的認知並沒有錯!
static void Main(string[] args)
{
var attr = (TargetFrameworkAttribute)Attribute.GetCustomAttribute(
typeof(Program).Assembly, typeof(TargetFrameworkAttribute));
Console.WriteLine($"Target Framework: {attr?.FrameworkName ?? "Unknown"}");
try
{
var wc = new WebClient();
var resp = wc.DownloadString("https://owasp.org/");
Console.WriteLine("HTTPS Request Succeeded.");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}

那為什麼 ASP.NET MVC 已升級 4.8,卻沒有如預期預設改用 TLS 1.2?
首先,複習幾個觀念:(參考資料:.NET 4.6 專案可以參照及使用 .NET 4.8 編譯的程式庫嗎?)
- .NET 4.X 採「就地更新」(In-Place Update)政策,在安裝 .NET 4.8 的環境, 以 .NET 4.0、4.5、4.6、4.7、4.8 為目標編譯的程式都是在 .NET 4.8 版 Runtime 執行。
- .NET 4.5 安裝時會覆寫換掉 \Windows.NET Framework\V4.0.30319 的 .NET 4 舊版 dll,沿用相同目錄同樣檔名,置換成新版本。因此,安裝 .NET 4.5 後,即使是以 .NET 4 為目標編譯的程式,使用的也會是 .NET 4.5 版 Runtime 及系統組件。而 .NET 4.5.X、4.6.X、4.7.X、4.8 的升級也是依循相同模式。
所以,不管我是編譯成 .NET 4.8 或 4.5.2,其實使用的都是 4.8 的 Runtime 與系統套件。而我們在 Visual Studio 選取不同的 Target Framework,它會修改 .csproj 的 <TargetFrameworkVersion> 以及 app.config 的 supportRuntime sku Attribute:



Visual Studio 在編譯時會依據 app.config 在執行檔加上 [assembly: TargetFramework(".NETFramework,Version=v4.X", FrameworkDisplayName = ".NET Framework 4.X")],程式在執行時便會向下相容,模擬舊版本的行為。
至於 ASP.NET 沒有 supportRuntime 可參照,所以會依據 web.config 的 compilation 及 httpRuntime 而定,問題主機的二者呈現衝突狀態,compilation 寫 4.8、但 httpRuntime 寫 4.5.2:
<system.web>
<compilation targetFramework="4.8" />
<httpRuntime targetFramework="4.5.2" />
</system.web>
compilation 的 targetFramework 決定 ASP.NET 動態編譯時的 .NET Framework 版本依據,設定 4.0,遇到 4.5+ 才支援的語法將會出錯。
httpRuntime 的 targetFramework 則決定執行時期的 .NET 版本行為 (CLR 的 Quirks Mode 行為),設定成 4.5.2 便會恢復 ServicePointManager.SecurityProtocol 預設舊版 SSL (非 TLS 1.2) 的行為,導致雖然 DLL 已升級成 4.8,在 ASP.NET 執行卻無法連上 TLS 1.2 網站的錯誤,結案!
關於這部分的詳細介紹,推薦 .NET 部落格這篇文章:All about httpRuntime targetFramework by levibroderick / .NET Blog。
.NET Framework TLS 1.2 defaults depend on both runtime and httpRuntime targetFramework. Upgrading DLLs isn’t enough—httpRuntime must also target 4.6+ for default TLS 1.2 behavior.
Comments
# by johnny
> 至於 ASP.NET Core 沒有 supportRuntime 可參照 Core 是不是筆誤?
# by Jeffrey
to johnny, Yes, 應該是 ASP.NET,不限 ASP.NET Core。謝謝指正。