這兩天處理了一宗 ASP.NET 3.5 程式無法連線另一台網站的案例。

案件摻雜了網路環境問題,解決掉一個網路設備設定錯誤後,得到 IE 連 HTTPS 可通但 .NET WebClient / PowerShell Invoke-WebRequest 無法連線 HTTPS 的結果,此時我卻發生空間迷向查錯地方,案情陷入焦著,只好約定隔日再戰。在回家的捷運上,我幡然覺醒罵了一聲幹,這根本經典的 TLS 問題啊! 我在耍什麼智障?

先整理背景知識:

  1. TLS 1.0/1.1 因安全疑慮被列為資安重點改善事項,這些幾資訊單位都會被要求停用 TLS 1.0/1.1,只留 TLS 1.2。
  2. 停用 TLS 1.0/1.1 不難,只需修改 Registry 即可,麻煩的是相關客戶端、軟體要一併升級調整不然會爆炸,以下是我的血淚史:

檢討本次鬼打牆的原因有二,一是與該主機為新安裝第一次連線,中間隔了防火牆、負載平衡器之類的設備,最開始先抓到一個網路設備設定錯誤,之後就杯弓蛇影,有任何異常都先懷疑網路有鬼。第二則是本次出現的錯誤訊息為 The unerlying connection was closed: An unexpected error occurred on a send. System.IO.IOException: Unable to read data from thye transport connection: An existing connection was forceibly closed by the remote host. 與記憶中看過的 TLS 錯誤訊息不太一樣。之前的經驗是:

  • 加密演算法不支援
    The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Authentication failed because the remote party has closed the transport stream
  • 對方網站停用 TLS 1.0
    The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Authentication failed because the remote party has closed the transport stream
  • SQL TLS 連線失敗
    A connection was successfully established with the server, but then an error occurred during the pre-login handshake.
    An existing connection was forcibly closed by the remote host.
  • OLE DB TLS 問題 SSL Security error.

這次的錯誤訊息只提到 An existing connection was forceibly closed by the remote host. 沒有 Authentication failed、SSL、TLS、Security 相關的字眼,加上網路設備有前科,我第一時間沒意識到是 TLS 1.2 問題,開了探照燈猛照網路設備逼它從實招來。直到推測可能與 TLS 1.2 有關後,再仔細看了錯誤訊息,發現 Stacktrace 其實是有線索的:

System.Net.WebException: The unerlying connection was closed: An unexpected error occurred on a send. --> System.IO.IOException: Unable to read data from thye transport connection: An existing connection was forceibly closed by the remote host.
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetsorkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
-- End of inner exception stack trace --
at System.Net.Sockets.NetowrkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.FixedSizeReader.ReadePacket(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessAuthenticatiokn(LazyAsyncResult lasyResult)
at System.Net.TlsStream.CallProcessAuthentication(Object state)
...

其中還是有 SslState、TlsStram 等關鍵字只以指認 TLS 是嫌犯。

測試在程式加上 ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; ASP.NET 3.5 程式即可成功連線,至此確認本次問題源自 .NET Framework 預設未使用 TLS 1.2。因相關環境已全部升級 TLS 1.2,我選擇依照微軟文件 - .NET Framework 的傳輸層安全性 (TLS) 最佳做法,套用 Registry:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v2.0.50727]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

套用後,重啟 IIS AppPool (不需重新開機),原本有問題的 ASP.NET 3.5 程式便順利連上目標 HTTPS 伺服器了。

本次學到的經驗:

  1. 連 HTTPS 看到 The unerlying connection was closed: An unexpected error occurred on a send. ,就算沒有 Authentication、Security、TLS 等字眼,也請優先排除 TLS 1.2 問題可能性。
  2. 把 Stacktrace 仔細看完啊,笨蛋!
  3. 套用 Registry,不用改程式,不需重開機,重啟程序/AppPool 即可讓 .NET 3.5/4.0 改用 TLS 1.2 連線。(注意:若你的環境仍有 TLS 1.x 服務就不適合用這招,需修改要連 TLS 1.2 的程式)

Experience of ASP.NET 3.5 failed to connect TLS 1.2 HTTPS service, which solved by adding registry.


Comments

Post a comment