Azure Cache for Redis: TLS 1.0 og 1.1 udgår snart

TLS 1.0 og 1.1 går snart på pension, Microsoft har derfor været så rare at dedikere en hel side til formålet.
TLDR: Frygt ej, der defaulter til TLS 1.2, hvis man ikke har angivet andet i klienten som forbinder til Redis cachen, som dokumentationen så smukt siger her:
Redis .NET clients use the earliest TLS version by default on .NET Framework 4.5.2 or earlier, and use the latest TLS version on .NET Framework 4.6 or later …. Redis .NET Core clients use the latest TLS version by default.
Hvilket i bund og grund betyder: bruger du .NET Core, eller .NET >=
4.6, er du homesafe, mens du på .NET <= 4.5.2 skal du huske at sætte
ssl=true,sslprotocols=tls12
hvis du bruger StackExchange.Redis, som
alle selvfølgelig gør (da det er en anbefaling fra Microsofts side, og
vi elsker at følge anbefalingerne som Microsoft kommer med!). Spøg til
side, StackExchange.Redis bliver brugt af Microsoft selv, og det er bare
en rimelig fed open source klient, som bliver backed af nogle kloge
hoveder, som ved hvad de gør.
Du undrer dig nok over overstående, ligesom mig! Gør du ikke det?
sslprotocols
, bliver parset til SslProtocols=som defaulter til =None
og som sikkert bliver brugt til
AuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean)=kaldet
i den underliggende =SslStream
, så hvorfor skal jeg dog bekymre mig om
det? Tjoo, men så har du nok ikke været helt så skarp til at læse
Microsofts dokumentation (som jeg hellere ikke har været i dette
tilfælde). Lad mig forklare.
Disclaimer: Overstående er faktisk kun den halve sandhed, da jeg ved at /=SslProtocols=* på /=ConfigurationOptions= er en nullable property type, som kan … ja defaulte til ingenting, og hvad sker der så egentlig hvis du i din connectionstring ikke angiver en TLS version? Hvordan håndterer StackExchange.Redis det? SPÆNDENDE!! Lad os dykke ned i koden! Og ja, StackExchange.Redis er open source, så vi kan rent faktisk dykke ned, eller som man siger i Enterprise: lave en deep dive, og få tilfredsstillet vores hunger efter sandheden. *
Din StackExchange.Redis klient gør som sagt brug af en connectionstring
til at forbinde til Redis serveren. Denne connectionstring kan bla
indeholde sslprotocols=tls11|tls12, ssl=true
, eller ssl=true
uden en
defineret sslprotocols
. Findes der en sslProtocols
, parses strengen
i ConfigurationOptions
, i denne metode:
internal static SslProtocols ParseSslProtocols(string key, string value)
{
//Flags expect commas as separators, but we need to use '|' since commas are already used in the connection string to mean something else
value = value?.Replace("|", ",");
if (!Enum.TryParse(value, true, out SslProtocols tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires an SslProtocol value (multiple values separated by '|').");
return tmp;
}
ParseSslProtocols
retur værdi gemmes i en Nullable property defineret
i selvsamme ConfigurationOptions
:
/// <summary> /// Configures which Ssl/TLS protocols should be allowed. If not set, defaults are chosen by the .NET framework. /// </summary> public SslProtocols? SslProtocols { get; set; }
Selve kommentaren kunne godt give os et hint om hvad der sker hvis
sslprotocols
ikke er defineret, dog er koden lidt mærkelig, for hvis
man sammenligner
System.Security.Authentication.SslProtocols
enum'en, som er defineret således
[Flags]
public enum SslProtocols
{
None = 0,
Ssl2 = 12,
Ssl3 = 48,
Tls = 192,
Default = 240,
Tls11 = 768,
Tls12 = 3072
}
med den SslProtocols
property som er defineret i
ConfigurationOptions
, og sammenholder den med overstående enum, så vil
man ved første øjekast tro at den defaulter til None
. Denne
SslProtocols
property bliver brugt i en extension metode til
SslStream
, ved navn AuthenticateAsClient
:
internal static void AuthenticateAsClient(this SslStream ssl, string host, SslProtocols? allowedProtocols, bool checkCertificateRevocation)
{
if (!allowedProtocols.HasValue)
{
//Default to the sslProtocols defined by the .NET Framework
AuthenticateAsClientUsingDefaultProtocols(ssl, host);
return;
}
var certificateCollection = new X509CertificateCollection();
ssl.AuthenticateAsClient(host, certificateCollection, allowedProtocols.Value, checkCertificateRevocation);
}
Kaldet hertil ser således ud
ssl.AuthenticateAsClient(host, config.SslProtocols, config.CheckCertificateRevocation);
hvor ssl
er en SslStream
og config
er vores
ConfigurationOptions
, og her sprænger kæden. SslProtocols
, defaulter
til None
, men SslProtocols
er en nullable, så er sslprotocols
ikke
defineret i ens connectionstring, indeholder SslProtocols
ingenting,
og kan derfor ikke defaulte til None
, og =None=/betyder/ at det er
op til det den underliggende styresystem, at vælge den bedste TLS
protokol, MEN FRYGT EJ, for kaldet til
AuthenticateAsClientUsingDefaultProtocols
kalder vitterligt bare
metoden AuthenticateAsClient
på SslStream
objektet. SslStream
er
beskrevet i stor detalje
her
og det er selve remark'en, som bringer glæde:
Starting with .NET Framework 4.7, this method authenticates using None, which allows the operating system to choose the best protocol to use, and to block protocols that are not secure. In .NET Framework 4.6 (and .NET Framework 4.5 with the latest security patches installed), the allowed TLS/SSL protocols versions are 1.2, 1.1, and 1.0 (unless you disable strong cryptography by editing the Windows Registry). No client certificates are used in the authentication. The certificate revocation list is not checked during authentication. The value specified for
targetHost
must match the name on the server's certificate.
Og så giver det hele lige pludselig mening. Det er lige nøjagtig derfor
StackExchange.Redis kan lave det some Nullable, og det er lige præcis
derfor Microsoft kan skrive som de gør i deres dokumentation omkring
pensionering af TLS 1.0 og TLS 1.1. Jeg gik nemlig udfra at den
eneste metode der fandtes på SslStream
var
AuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean)
og jeg blev forvirret af StackExchange.Redis' Nullable SslProtocols
,
da den netop ikke kan falde tilbage til None
. Forvirringen var
komplet.
Jeg lærte lidt i dag; Det håber jeg også du gjorde.
Det næste du skal gøre, er at se om du falder indenfor det segment, som
enten skal tilføje sslProtocols=tls12, ssl=true
eller lave de fornødne
rettelser, hvis du bruger ServiceStack.Redis. Bruger du .NET Core bruger
du allerede seneste TLS version.
Aftermath
Jeg har dog oprettet et issue ved Microsoft som spørger lidt mere ind til ordlyden på deres måde at sige at man kan være sikker på at seneste TLS version bliver brugt, hvis bare man bruger .NET Core, dog er jeg rimelig sikker på at man siden juni 2018 i Azure (og faktisk siden Windows Server 2016) har shipped med TLS 1.2. Vil man se hvad ens webapp kører med kan man:
- vælg webapp resourcen
- Via menuen, klik "TLS/SSL Settings" under Settings
Her vil der stå hvad TLS version webapp'en bruger under Mimimum TLS Version. Har man ikke rørt noget ved webapp'ens settings, og er ens webapp født efter juni 2018, så burde den defaulte til TLS 1.2.