針對不支援 TLS 1.2 的應用程式進行疑難排解
重要
截至 2024 年 9 月 1 日,所有客戶的雲端服務 (傳統) 均已淘汰。 自 2024 年 10 月起,Microsoft 將停止並關閉任何執行中的現有部署,且資料將永久遺失。 新部署應該使用 Azure Resource Manager 型的新部署模型 Azure 雲端服務 (延伸支援)。
本文會說明如何啟用舊版 TLS 通訊協定 (TLS 1.0 和 1.1)。 它也涵蓋舊版加密套件的應用程式,以支援 Windows Server 2019 雲端服務 Web 和背景工作角色上的其他通訊協定。
我們了解雖然我們正在採取步驟淘汰 TLS 1.0 和 TLS 1.1,但在此期間,我們的客戶仍需要舊版通訊協定和加密套件的支援。 雖然我們不建議重新啟用這些舊版值,但是我們會提供指引來協助客戶。 我們鼓勵客戶在實作本文中所述的變更之前,先評估迴歸的風險。
注意
客體作業系統系列 6 版本藉由明確停用 TLS 1.0 和1.1,並定義一組特定的加密套件,來強制執行 TLS 1.2。如需有關客體作業系統系列的詳細資訊,請參閱客體作業系統發佈新聞
捨棄 TLS 1.0、TLS 1.1 和舊版加密套件的支援
為了支援我們對使用同級產品中最佳加密的承諾,Microsoft 宣佈了在 2017 年 6 月從 TLS 1.0 和 1.1 開始移轉的計劃。 Microsoft 宣佈我們的目的是在 2020 上半年停用 Microsoft Edge 和 Internet Explorer 11 預設支援的傳輸層安全性 (TLS) 1.0 和 1.1 版本。 Apple、Google 和 Mozilla 也發佈類似宣告,表示這是產業的方向。
如需詳細資訊,請參閱在 Microsoft Azure 中準備 TLS 1.2
TLS 設定
Windows Server 2019 雲端伺服器映像設定了在登錄層級停用 TLS 1.0 和 TLS 1.1。 這表示部署到這個 Windows 版本的應用程式,以及使用 Windows 堆疊進行 TLS 交涉的應用程式,不會允許 TLS 1.0 和 TLS 1.1 通訊。
伺服器也會隨附一組有限的加密套件:
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
步驟 1:建立 PowerShell 指令碼以啟用 TLS 1.0 和 TLS 1.1
使用下列程式碼作為範例,以建立可啟用舊版通訊協定和加密套件的指令碼。 基於本文件的目的,此指令碼將會命名為:TLSsettings.ps1。 在您的本機桌面上儲存此指令碼,以便在稍後的步驟中輕鬆存取。
# You can use the -SetCipherOrder (or -sco) option to also set the TLS cipher
# suite order. Change the cipherorder variable below to the order you want to set on the
# server. Setting this requires a reboot to take effect.
Param(
[parameter(Mandatory=$false)]
[alias("sco")]
[switch]$SetCipherOrder)
Function DisableRC4 {
param ( $restart)
$subkeys = Get-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL"
$ciphers = $subkeys.OpenSubKey("Ciphers", $true)
if($ciphers.SubKeyCount -eq 0) {
$k1 = $ciphers.CreateSubKey("RC4 128/128")
$k1.SetValue("Enabled", 0, [Microsoft.Win32.RegistryValueKind]::DWord)
$restart = $true
$k2 = $ciphers.CreateSubKey("RC4 64/128")
$k2.SetValue("Enabled", 0, [Microsoft.Win32.RegistryValueKind]::DWord)
$k3 = $ciphers.CreateSubKey("RC4 56/128")
$k3.SetValue("Enabled", 0, [Microsoft.Win32.RegistryValueKind]::DWord)
$k4 = $ciphers.CreateSubKey("RC4 40/128")
$k4.SetValue("Enabled", 0, [Microsoft.Win32.RegistryValueKind]::DWord)
}
$restart
}
Function Set-CryptoSetting {
param (
$keyindex,
$value,
$valuedata,
$valuetype,
$restart
)
# Check for existence of registry key, and create if it does not exist
If (!(Test-Path -Path $regkeys[$keyindex])) {
New-Item $regkeys[$keyindex] | Out-Null
}
# Get data of registry value, or null if it does not exist
$val = (Get-ItemProperty -Path $regkeys[$keyindex] -Name $value -ErrorAction SilentlyContinue).$value
If ($null -eq $val) {
# Value does not exist - create and set to desired value
New-ItemProperty -Path $regkeys[$keyindex] -Name $value -Value $valuedata -PropertyType $valuetype | Out-Null
$restart = $True
Write-Host "Configuring $regkeys[$keyindex]...."
} Else {
# Value does exist - if not equal to desired value, change it
If ($val -ne $valuedata) {
Set-ItemProperty -Path $regkeys[$keyindex] -Name $value -Value $valuedata
$restart = $True
Write-Host "Configuring $regkeys[$keyindex]..."
}
}
$restart
}
$regkeys = @(
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server", #2
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client", #4
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2", #6
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server", #8
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client", #10
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0", #12
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server", #14
"HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002"
)
Function Set-Windows10PlusCurveOrder {
param ( $reboot)
$desiredOrder = "NistP384;NistP256".Split(";")
If ([Environment]::OSVersion.Version.Major -ge 10) {
If (!(Test-Path -Path $regkeys[15])) {
New-Item $regkeys[15] | Out-Null
$reboot = $True
}
$val = (Get-Item -Path $regkeys[15] -ErrorAction SilentlyContinue).GetValue("EccCurves", $null)
if( $null -eq $val) {
New-ItemProperty -Path $regkeys[15] -Name EccCurves -Value $desiredOrder -PropertyType MultiString | Out-Null
$reboot = $True
} else {
if ([System.String]::Join(';', $val) -ne [System.String]::Join(';', $desiredOrder)) {
Write-Host "The original curve order ", `n, $val, `n, "needs to be updated to ", $desiredOrder
Set-ItemProperty -Path $regkeys[15] -Name EccCurves -Value $desiredOrder
$reboot = $True
}
}
}
$reboot
}
If ([Environment]::OSVersion.Version.Major -lt 10) {
# This is for Windows before 10
Write-Host "Configuring Windows before 10..."
$cipherorder = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384_P384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256_P256,"
$cipherorder += "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384_P384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256_P256,"
$cipherorder += "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256,"
$cipherorder += "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P256,"
$cipherorder += "TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256,"
$cipherorder += "TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,"
$cipherorder += "TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA"
} Else {
# this is for windows 10 or above
Write-Host "Configuring Windows 10+..."
$cipherorder = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,"
$cipherorder += "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,"
$cipherorder += "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,"
$cipherorder += "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,"
$cipherorder += "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,"
$cipherorder += "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,"
$cipherorder += "TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256,"
$cipherorder += "TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,"
$cipherorder += "TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA"
}
# If any settings are changed, this will change to $True and the server will reboot
$reboot = $False
# Check for existence of registry keys (SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.1, TLS 1.2), and create if they do not exist
For ($i = 0; $i -le 14; $i = $i + 1) {
If (!(Test-Path -Path $regkeys[$i])) {
New-Item $regkeys[$i] | Out-Null
}
}
# Ensure SSL 2.0 disabled for client/server
$reboot = Set-CryptoSetting 10 DisabledByDefault 1 DWord $reboot
$reboot = Set-CryptoSetting 10 Enabled 0 DWord $reboot
$reboot = Set-CryptoSetting 11 DisabledByDefault 1 DWord $reboot
$reboot = Set-CryptoSetting 11 Enabled 0 DWord $reboot
# Ensure SSL 3.0 disabled for client/server
$reboot = Set-CryptoSetting 13 DisabledByDefault 1 DWord $reboot
$reboot = Set-CryptoSetting 13 Enabled 0 DWord $reboot
$reboot = Set-CryptoSetting 14 DisabledByDefault 1 DWord $reboot
$reboot = Set-CryptoSetting 14 Enabled 0 DWord $reboot
# Ensure TLS 1.0 enabled for client/server
$reboot = Set-CryptoSetting 1 DisabledByDefault 0 DWord $reboot
$reboot = Set-CryptoSetting 1 Enabled 1 DWord $reboot
$reboot = Set-CryptoSetting 2 DisabledByDefault 0 DWord $reboot
$reboot = Set-CryptoSetting 2 Enabled 1 DWord $reboot
# Ensure TLS 1.1 enabled for client/server
$reboot = Set-CryptoSetting 4 DisabledByDefault 0 DWord $reboot
$reboot = Set-CryptoSetting 4 Enabled 1 DWord $reboot
$reboot = Set-CryptoSetting 5 DisabledByDefault 0 DWord $reboot
$reboot = Set-CryptoSetting 5 Enabled 1 DWord $reboot
# Ensure TLS 1.2 enabled for client/server
$reboot = Set-CryptoSetting 7 DisabledByDefault 0 DWord $reboot
$reboot = Set-CryptoSetting 7 Enabled 1 DWord $reboot
$reboot = Set-CryptoSetting 8 DisabledByDefault 0 DWord $reboot
$reboot = Set-CryptoSetting 8 Enabled 1 DWord $reboot
$reboot = DisableRC4($reboot)
If ($SetCipherOrder) {
If (!(Test-Path -Path $regkeys[15])) {
New-Item $regkeys[15] | Out-Null
$reboot = $True
}
$val = (Get-Item -Path $regkeys[15] -ErrorAction SilentlyContinue).GetValue("Functions", $null)
if ($val -ne $cipherorder)
{
Write-Host "The original cipher suite order needs to be updated", `n, $val
Set-ItemProperty -Path $regkeys[15] -Name Functions -Value $cipherorder
$reboot = $True
}
}
$reboot = Set-Windows10PlusCurveOrder $reboot
If ($reboot) {
# Randomize the reboot timing since it could be run in a large cluster.
$tick = [System.Int32]([System.DateTime]::Now.Ticks % [System.Int32]::MaxValue)
$rand = [System.Random]::new($tick)
$sec = $rand.Next(30, 600)
Write-Host "Rebooting after", $sec, " second(s)..."
Write-Host "shutdown.exe /r /t $sec /c ""Crypto settings changed"" /f /d p:2:4"
shutdown.exe /r /t $sec /c "Crypto settings changed" /f /d p:2:4
} Else {
Write-Host "Nothing get updated."
}
步驟 2:建立命令檔案
使用以下指令碼建立名為 RunTLSSettings.cmd 的 CMD 檔案。 在您的本機桌面上儲存此指令碼,以便在稍後的步驟中輕鬆存取。
SET LOG_FILE="%TEMP%\StartupLog.txt"
SET EXECUTE_PS1=0
IF "%ComputeEmulatorRunning%" == "" (
SET EXECUTE_PS1=1
)
IF "%ComputeEmulatorRunning%" == "false" (
SET EXECUTE_PS1=1
)
IF %EXECUTE_PS1% EQU 1 (
echo "Invoking TLSsettings.ps1 on Azure service at %TIME% on %DATE%" >> %LOG_FILE% 2>&1
PowerShell -ExecutionPolicy Unrestricted %~dp0TLSsettings.ps1 -sco >> %LOG_FILE% 2>&1
) ELSE (
echo "Skipping TLSsettings.ps1 invocation on emulated environment" >> %LOG_FILE% 2>&1
)
EXIT /B %ERRORLEVEL%
步驟 3:將啟動工作新增至角色的服務定義 (csdef)
將下列程式碼片段新增至您現有的服務定義檔。
<Startup>
<Task executionContext="elevated" taskType="simple" commandLine="RunTLSSettings.cmd">
</Task>
</Startup>
以下範例顯示背景工作角色和 Web 角色。
<?xmlversion="1.0" encoding="utf-8"?>
<ServiceDefinitionname="CloudServiceName" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2015-04.2.6">
<WebRolename="WebRole1" vmsize="Standard_D1_v2">
<Sites>
<Sitename="Web">
<Bindings>
<Bindingname="Endpoint1" endpointName="Endpoint1"/>
</Bindings>
</Site>
</Sites>
<Startup>
<Task executionContext="elevated" taskType="simple" commandLine="RunTLSSettings.cmd">
</Task>
</Startup>
<Endpoints>
<InputEndpointname="Endpoint1" protocol="http" port="80"/>
</Endpoints>
</WebRole>
<WorkerRolename="WorkerRole1" vmsize="Standard_D1_v2">
<Startup>
<Task executionContext="elevated" taskType="simple" commandLine="RunTLSSettings.cmd">
</Task>
</Startup>
</WorkerRole>
</ServiceDefinition>
步驟 4:將指令碼新增至您的雲端服務
- 在 Visual Studio 中,以滑鼠右鍵按一下您的 WebRole 或 WorkerRole
- 選取新增
- 選取 [現有項目]
- 在 [檔案總管] 中,瀏覽至您在其中儲存 TLSsettings.ps1 和 RunTLSSettings.cmd 檔案的桌面
- 選取兩個檔案,將其新增至您的雲端服務專案
步驟 5:啟用複製至輸出目錄
為了確保指令碼隨著每次從 Visual Studio 推送的更新上傳,設定 [複製到輸出目錄] 必須設定為 [一律複製]
- 在您的 WebRole 或 WorkerRole 底下,以滑鼠右鍵按一下 RunTLSSettings.cmd
- 選取 [屬性]
- 在 [屬性] 索引標籤中,將 [複製到輸出目錄] 設定為 [一律複製]
- 針對 TLSsettings.ps1 重複步驟
步驟 6:發佈和驗證
完成上述步驟後,請將更新發佈至現有的雲端服務。
您可以使用 SSLLabs 來驗證端點的 TLS 狀態