111

I am trying to mitigate our vulnerability to the Poodle SSL 3.0 Fallback attack. Our admins have already started disabling SSL in favor of TLS for inbound connections to our servers. And we have also advised our team to disable SSL in their web browsers. I'm now looking at our .NET codebase, which initiates HTTPS connections with various services through System.Net.HttpWebRequest. I believe that these connections could be vulnerable to a MITM attack if they allow fallback from TLS to SSL. Here is what I have determined so far. Could some one please double-check this to verify that I am right? This vulnerability is brand new, so I have yet to see any guidance from Microsoft on how to mitigate it in .NET:

  1. The allowed protocols for the System.Net.Security.SslStream class, which underpins secure communication in .NET, are set globally for each AppDomain via the System.Net.ServicePointManager.SecurityProtocol property.

  2. The default value of this property in .NET 4.5 is Ssl3 | Tls (although I can't find documentation to back that up.) SecurityProtocolType is an enum with the Flags attribute, so it's a bitwise OR of those two values. You can check this in your environment with this line of code:

    Console.WriteLine(System.Net.ServicePointManager.SecurityProtocol.ToString());

  3. This should be changed to just Tls, or perhaps Tls12, before you initiate any connections in your app:

    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls;

  4. Important: Since the property supports multiple bitwise flags, I assume that the SslStream will not automatically fallback to other unspecified protocols during handshake. Otherwise, what would be the point of supporting multiple flags?

Update on TLS 1.0 vs 1.1/1.2:

According to Google security expert Adam Langley, TLS 1.0 was later found to be vulnerable to POODLE if not implemented correctly, so you should consider moving to TLS 1.2 exclusively.

Update for .NET Framework 4.7 and above:

As alluded to by Prof Von Lemongargle below, starting with version 4.7 of the .NET Framework, there is no need to use this hack as the default setting will allow the OS to choose the most secure TLS protocol version. See Transport Layer Security (TLS) best practices with the .NET Framework for more information.

Dale K
  • 25,246
  • 15
  • 42
  • 71
Jordan Rieger
  • 3,025
  • 3
  • 30
  • 50
  • 1
    Regarding point 2 above: see https://referencesource.microsoft.com/#System/net/System/Net/SecureProtocols/SslEnumTypes.cs,c7e989c42228556a SslProtocols " Default = Ssl3 | Tls" – Dai Bok Nov 11 '16 at 09:51
  • 1
    @Dai Bok The default now is SystemDefault option https://learn.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.8 – MarwaAhmad Jan 20 '20 at 15:34
  • @MarwaAhmad if that is the case, the source code reference in my link does not reflect default (48 | 192) . If set to none ( 0 ) , it should revert to the system default. This smells to me and I'd probably test this before making changes as it may lead to missconfiguration...and turning off protocols on the wrong .net framework... – Dai Bok Jan 21 '20 at 11:29

6 Answers6

139

We are doing the same thing. To support only TLS 1.2 and no SSL protocols, you can do this:

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

SecurityProtocolType.Tls is only TLS 1.0, not all TLS versions.

As a side: If you want to check that your site does not allow SSL connections, you can do so here (I don't think this will be affected by the above setting, we had to edit the registry to force IIS to use TLS for incoming connections): https://www.ssllabs.com/ssltest/index.html

To disable SSL 2.0 and 3.0 in IIS, see this page: https://www.sslshopper.com/article-how-to-disable-ssl-2.0-in-iis-7.html

Brad C
  • 2,868
  • 22
  • 33
Eddie Fletcher
  • 2,823
  • 2
  • 21
  • 23
  • Right. Good idea to support the newer TLS versions too: future-proofing. – Jordan Rieger Oct 15 '14 at 21:51
  • 1
    And for the benefit of others, the registry edit you mentioned can also be done with these steps: http://serverfault.com/a/637263/98656. In Windows Server 2003 to 2012 R2 the protocols are controlled by flags at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols. To disable SSLv3, create a subkey at the above location named 'SSL 3.0' and, under that, a subkey named 'Server' and, under there, a DWORD value named 'Enabled', set at 0. You should also disable SSL 2.0 in the same way. – Jordan Rieger Oct 15 '14 at 22:02
  • Thanks, I also just added a link to the webpage I was using that describes how to do that – Eddie Fletcher Oct 15 '14 at 22:04
  • Hi - I want to add TLS 1.2 capability to my IIS server on Windows Server 2008 R2. I'm looking at the registry area you mention and have disabled both SSL 2.0 and 3.0. The part I don't understand: When you say, "You can do this" followed by "System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;" I'm wondering where I put that? Do I pull up a command prompt and type it there? Thanks! – ScotterMonkey Oct 27 '14 at 15:46
  • 4
    @ScotterMonkey You only need to set System.Net.ServicePointManager.SecurityProtocol if you are initiating *outbound* connections from .NET code, e.g. connecting to a web service or API from custom code running on your server. If you are only running a simple web site, and you are only accepting *incoming* connections from browsers, then the registry fix is enough. – Jordan Rieger Oct 27 '14 at 21:30
  • 7
    Note: It appears `SecurityProtocolType.Tls11` and `SecurityProtocolType.Tls12` enum values are only available in ASP.net 4.5 and up. Not sure what we would have to do for older code bases running on 2.0 if TLS 1.0 goes by the wayside. – Sam Oct 28 '14 at 16:31
  • It appears that SSLv3 fallback is still enabled with this method. – Kraang Prime May 03 '15 at 19:21
  • @SanuelJackson Do you have anything to support that? E.g. logs from the server receiving the connection, or packet analysis on the server initiating the connection. – Jordan Rieger May 12 '15 at 17:12
  • @JordanRieger if you check the link i was given, there is enough evidence to show that it is possible. More so, one of my card processors I was dealing with was able to determine if I was connecting to them using SSL or TLS --- and beyond that, look at the protocol itself man. There is a handshake at which point the client requests a security level, and the server responds with it + algos supported . It's a no brainer that you should be able to determine the communication level used when serving pages since they need to be ciphered before sending to the client. – Kraang Prime May 12 '15 at 18:59
  • @JordanRieger - and if you are looking for something more official-ish, http://security.stackexchange.com/questions/5607/how-can-i-determine-the-encryption-strength-of-an-ssl-connection . That is a language independent version of this question (duh, it's on exchange, not overflow). As for those who vote to close a question because the complexity is outside their scope, that is pretty lame, please clarify why you think this clear question was unclear -- and not why you were confused by it ;) – Kraang Prime May 12 '15 at 19:04
  • @SanuelJackson What do you mean by "if you check the link i was given"? What link are you referring to? Also, I don't understand why the question you referenced is relevant to this question, which concerns *how to ensure that .NET HttpWebRequest client does not accept certain protocol fallbacks during handshake.* – Jordan Rieger May 12 '15 at 21:53
  • @JordanRieger - sorry, I was thinking I was responding to a different question. My bad. I asked another question elsewhere regarding having a server side method for detecting the connection handshake established between client and server that is not dependant on javascript or browser specific crap -- pure server side detection of what was finally decided. Somehow, my responses ended up here even tho i posted to that question -.- – Kraang Prime May 12 '15 at 22:46
  • @SanuelJackson No worries, I thought it was something like that. – Jordan Rieger May 12 '15 at 23:14
  • Anyone happen to know how to add this to a windows store app (Windows phone / tablet apps) where servicepointmanager class is unavailable? – Watson Jul 17 '15 at 16:51
  • This is a solution for .Net 4.5+ as the enums for SecurityProtocolType includes SecurityProtocolType.Tls11 and SecurityProtocolType.Tls12. But what about .Net 4.0 and lower versions? any solutions? – Ehsan Oct 30 '15 at 13:57
  • Per the update at the bottom of the original question, it would make sense to remove 1.0 and 1.1 values. – Neil Dec 04 '15 at 22:59
  • Doesn't this depend on the version of .NET, as well as the OS your application is running on? – Don Cheadle Mar 08 '16 at 22:48
  • @Sam - Part of my confusion: this code change simply ensures out-going requests -- regardless of Windows version -- will have `TLS 1.2` - correct? And for ensuring in-coming requests use `TLS 1.2`, then you *do* need to be concerned with Windows version (for it will actually handle that level of HTTP) - correct? – Don Cheadle Mar 09 '16 at 18:35
  • Yes this code will only affect outbound calls from your app to a third party. To force inbound connections to use TLS you must follow the instructions here: https://www.sslshopper.com/article-how-to-disable-ssl-2.0-in-iis-7.html – Eddie Fletcher Mar 09 '16 at 23:52
  • Where can we add this line of code ? `System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;` – Anish V Jun 06 '16 at 11:28
  • 1
    @AnishV You put that line of code in the initialization of your app, before any code that initiates an outbound SSL/TLS connection. – Jordan Rieger Oct 11 '16 at 23:22
  • You specify this in the client application, to be clear. – K0D4 Apr 24 '20 at 18:25
25

@Eddie Loeffen's answer seems to be the most popular answer to this question, but it has some bad long term effects. If you review the documentation page for System.Net.ServicePointManager.SecurityProtocol here the remarks section implies that the negotiation phase should just address this (and forcing the protocol is bad practice because in the future, TLS 1.2 will be compromised as well). However, we wouldn't be looking for this answer if it did.

Researching, it appears that the ALPN negotiation protocol is required to get to TLS1.2 in the negotiation phase. We took that as our starting point and tried newer versions of the .Net framework to see where support starts. We found that .Net 4.5.2 does not support negotiation to TLS 1.2, but .Net 4.6 does.

So, even though forcing TLS1.2 will get the job done now, I recommend that you upgrade to .Net 4.6 instead. Since this is a PCI DSS issue for June 2016, the window is short, but the new framework is a better answer.

UPDATE: Working from the comments, I built this:

ServicePointManager.SecurityProtocol = 0;    
foreach (SecurityProtocolType protocol in SecurityProtocolType.GetValues(typeof(SecurityProtocolType)))
    {
        switch (protocol)
        {
            case SecurityProtocolType.Ssl3:
            case SecurityProtocolType.Tls:
            case SecurityProtocolType.Tls11:
                break;
            default:
                ServicePointManager.SecurityProtocol |= protocol;
            break;
        }
    }

In order to validate the concept, I or'd together SSL3 and TLS1.2 and ran the code targeting a server that supports only TLS 1.0 and TLS 1.2 (1.1 is disabled). With the or'd protocols, it seems to connect fine. If I change to SSL3 and TLS 1.1, that failed to connect. My validation uses HttpWebRequest from System.Net and just calls GetResponse(). For instance, I tried this and failed:

        HttpWebRequest request = WebRequest.Create("https://www.contoso.com/my/web/resource") as HttpWebRequest;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls11;
        request.GetResponse();

while this worked:

        HttpWebRequest request = WebRequest.Create("https://www.contoso.com/my/web/resource") as HttpWebRequest;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
        request.GetResponse();

This has an advantage over forcing TLS 1.2 in that, if the .Net framework is upgraded so that there are more entries in the Enum, they will be supported by the code as is. It has a disadvantage over just using .Net 4.6 in that 4.6 uses ALPN and should support new protocols if no restriction is specified.

Edit 4/29/2019 - Microsoft published this article last October. It has a pretty good synopsis of their recommendation of how this should be done in the various versions of .net framework.

Prof Von Lemongargle
  • 3,658
  • 31
  • 29
  • 3
    Interesting. It looks like the documentation page was updated along with the MSDN move to ".NET Framework **(current version)**" and now explains that "no default value is listed for this property, on purpose." Perhaps a more future-proof version of the accepted answer would be to first query the property to retrieve the list of allowed protocols, and then remove the ones that your app considers insecure (i.e. anything less than TLS 1.2) rather than explicitly adding only the ones that you deem secure. This would not exclude new versions in the future. – Jordan Rieger Mar 07 '16 at 23:10
  • It would be good to be able to have the program remove protocols from a list that the system generates, but I don't see a way to do that in the current API. The only option seems to be to force the specific protocol, which I don't see why anyone would desire to do. Unfortunately, without the ALPN support, it seems to be the only way to get a TLS1.2 connection to work. – Prof Von Lemongargle Mar 09 '16 at 18:37
  • 1
    It should be possible to loop through all the SecurityProtocolType enums, see which ones are present in the ServicePointManager.SecurityProtocol flags enum (it's a logical OR of all set flags, so you can test each one with an AND), and then build a new list of them with the ones you don't want removed. Then combine those into one enum and set the property with that. – Jordan Rieger Mar 09 '16 at 19:00
  • I was just re-reading your enum/looping code, and I think it's incorrect. The |= logical operator will result in the property including whatever default values were set in the property initially, rather than including only Tls12 and above. To fix it, you have to initialize a SecurityProtocolType enum variable with a value of 0, do the |= loop against that variable, and then assign it to the ServicePointManager.SecurityProtocol property afterward. – Jordan Rieger Oct 11 '16 at 23:30
  • 7
    Does anyone else find it insane that .NET doesn't automatically negotiate the highest protocol version it is capable of?! – Raman May 26 '17 at 23:44
  • @Raman I know this comment is old, but indeed, why in the world is that? How do we avoid having to track down every instance of hard-coded TLS version when v1.3 and beyond are released? – k3davis Jun 13 '18 at 16:40
  • @k3davis Very good question. I tried very hard to find a solution to this that involved *not* hard-coding the TLS version for that very reason. I finally found two registry entries that seemed to do the trick in my situation. See the answer I just added here: https://stackoverflow.com/a/50845570/430128 – Raman Jun 13 '18 at 20:19
7

I had to cast the integer equivalent to get around the fact that I'm still using .NET 4.0

System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
/* Note the property type  
   [System.Flags]
   public enum SecurityProtocolType
   {
     Ssl3 = 48,
     Tls = 192,
     Tls11 = 768,
     Tls12 = 3072,
   } 
*/
CZahrobsky
  • 762
  • 7
  • 7
  • This works but .NET 4.0 doesn't support TLS 1.2, however .NET 4.5 and higher support TLS 1.2. So you can add this code (or add do a bitwise OR to add support for TLS 1.2 negotiation) to your .NET 4.0 application and build it but you will need to deploy the code on .NET 4.5 or higher. https://blogs.perficient.com/microsoft/2016/04/tsl-1-2-and-net-support/ – rboy Dec 22 '17 at 05:00
  • Also see this, .NET 4.0 code will work fine on higher version of .NET including .NET 4.5 and .NET 4.6 https://stackoverflow.com/questions/33378902/can-ms-net-4-6-be-used-with-visual-studio-2010 – rboy Dec 22 '17 at 05:01
  • The integer cast worked for sending TLS 1.2. The Tls12 enum value just doesn't exist in. NET 4.0 – CZahrobsky Dec 23 '17 at 07:41
  • Two different points. See my comment. You can put the value but if your runtime Net version is 4.0 it will fail. You can compile with this flag but your runtime .NET versions needs to be 4.5 or higher as the base components on .NET 4.0 don't support the ciphers required for TLS 1.2 (see the links) – rboy Dec 24 '17 at 07:44
  • 2
    There are several hotfixes for older .NET runtime versions to add TLS 1.2 support. e.g. https://support.microsoft.com/en-us/help/3154518/support-for-tls-system-default-versions-included-in-the--net-framework, CZahrobsky could have also been using this on the Win10 fall creators update which also included the hotfix. – silent tone Jan 30 '18 at 00:08
6

@watson

On windows forms it is available, at the top of the class put

  static void Main(string[] args)
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
       //other stuff here
    }

since windows is single threaded, its all you need, in the event its a service you need to put it right above the call to the service (since there is no telling what thread you'll be on).

using System.Security.Principal 

is also needed.

DirtyHowi
  • 111
  • 1
  • 5
5

If you're curious which protocols .NET supports, you can try HttpClient out on https://www.howsmyssl.com/

// set proxy if you need to
// WebRequest.DefaultWebProxy = new WebProxy("http://localhost:3128");

File.WriteAllText("howsmyssl-httpclient.html", new HttpClient().GetStringAsync("https://www.howsmyssl.com").Result);

// alternative using WebClient for older framework versions
// new WebClient().DownloadFile("https://www.howsmyssl.com/", "howsmyssl-webclient.html");

The result is damning:

Your client is using TLS 1.0, which is very old, possibly susceptible to the BEAST attack, and doesn't have the best cipher suites available on it. Additions like AES-GCM, and SHA256 to replace MD5-SHA-1 are unavailable to a TLS 1.0 client as well as many more modern cipher suites.

As Eddie explains above, you can enable better protocols manually:

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11; 

I don't know why it uses bad protocols out-the-box. That seems a poor setup choice, tantamount to a major security bug (I bet plenty of applications don't change the default). How can we report it?

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
1

I found the simplest solution is to add two registry entries as follows (run this in a command prompt with admin privileges):

reg add HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 /v SchUseStrongCrypto /t REG_DWORD /d 1 /reg:32

reg add HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 /v SchUseStrongCrypto /t REG_DWORD /d 1 /reg:64

These entries seem to affect how the .NET CLR chooses a protocol when making a secure connection as a client.

There is more information about this registry entry here:

https://learn.microsoft.com/en-us/security-updates/SecurityAdvisories/2015/2960358#suggested-actions

Not only is this simpler, but assuming it works for your case, far more robust than a code-based solution, which requires developers to track protocol and development and update all their relevant code. Hopefully, similar environment changes can be made for TLS 1.3 and beyond, as long as .NET remains dumb enough to not automatically choose the highest available protocol.

NOTE: Even though, according to the article above, this is only supposed to disable RC4, and one would not think this would change whether the .NET client is allowed to use TLS1.2+ or not, for some reason it does have this effect.

NOTE: As noted by @Jordan Rieger in the comments, this is not a solution for POODLE, since it does not disable the older protocols a -- it merely allows the client to work with newer protocols e.g. when a patched server has disabled the older protocols. However, with a MITM attack, obviously a compromised server will offer the client an older protocol, which the client will then happily use.

TODO: Try to disable client-side use of TLS1.0 and TLS1.1 with these registry entries, however I don't know if the .NET http client libraries respect these settings or not:

https://learn.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#tls-10

https://learn.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#tls-11

Raman
  • 17,606
  • 5
  • 95
  • 112
  • Registry settings as an alternative to a code would be great, but do these settings actually disable TLS 1.0 and 1.1 in favor of only allowing client connections using TLS 1.2 and above? According to the link, it seems to only disable RC4 in TLS. I think the Poodle attack is broader than that. – Jordan Rieger Jun 13 '18 at 23:36
  • @JordanRieger These registry entries allow a .NET client to connect to a server that has the older protocols disabled to mitigate POODLE. Without these, the client will throw an error as the .NET client stupidly insists on using an older protocol even when the server is asking for a newer one. All bets are off if your server still allows connections using the older protocols. Theoretically the older protocols *should* be disabled. I wonder if the same registry entries that allow disabling these on the server (e.g. https://www.nartac.com/Products/IISCrypto) would work for the client too? – Raman Jun 14 '18 at 00:27
  • In the registry entries that nartac manipulates, there are separate settings for (when acting as) client and (when acting as) server. I don't recall if their interface distinguishes however. Do you know what versions of .net support this registry hack? – Prof Von Lemongargle Jun 15 '18 at 18:12
  • @Raman the point of my question, though, was to mitigate the POODLE vulnerability when your client is connecting to a server that you don't control. The vulnerability will allow a MITM attacker to downgrade the protocol to an older TLS or SSL version which can be hacked. Just disabling RC4 is not sufficient. These registry settings might be useful for certain cases, but not the scenario in my question. – Jordan Rieger Jun 15 '18 at 18:49
  • 1
    @JordanRieger Agreed. I updated the text with some additional observations and thoughts. – Raman Jun 15 '18 at 19:48