如何在.NET中禁用SSL回退并仅使用TLS进行出站连接?

How do I disable SSL fallback and use only TLS for outbound connections in .NET? (Poodle mitigation)

我正在尝试减轻Poodle SSL3.0回退攻击的脆弱性。我们的管理员已经开始禁用ssl,而对我们的服务器的入站连接使用tls。我们还建议我们的团队在他们的Web浏览器中禁用SSL。我现在看看我们的.NET代码库,它通过system.net.httpwebrequest启动与各种服务的HTTPS连接。我相信,如果这些连接允许从TLS回退到SSL,那么它们可能容易受到MITM攻击。这是我到目前为止所决定的。有人能再检查一下这个吗?这个漏洞是全新的,所以我还没有看到微软关于如何在.NET中减轻它的任何指导:

  • 支持.NET中安全通信的System.NET.Security.SSLStream类的允许协议通过System.NET.ServicePointManager.SecurityProtocol属性为每个AppDomain全局设置。

  • .NET 4.5中此属性的默认值是Ssl3 | Tls(尽管我找不到支持该值的文档。)SecurityProtocolType是一个带有Flags属性的枚举,因此它是这两个值的按位或。您可以使用以下代码行在您的环境中检查:

    console.writeline(system.net.servicePointManager.securityProtocol.toString());

  • 在您启动应用程序中的任何连接之前,应该将其更改为仅Tls或可能是Tls12

    system.net.servicePointManager.securityProtocol=system.net.securityProtocolType.tls;

  • 重要提示:由于该属性支持多个位标志,我假设sslstream在握手期间不会自动回退到其他未指定的协议。否则,支持多个标志的意义是什么?

  • TLS 1.0与1.1/1.2的更新:

    据谷歌安全专家亚当·兰利(AdamLangley)称,如果没有正确实施,tls 1.0很容易受到poodle的攻击,因此您应该考虑只使用tls1.2。


    我们也在做同样的事情。要仅支持TLS 1.2且不支持SSL协议,可以执行以下操作:

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

    securityProtocolType.tls只是tls 1.0,不是所有的tls版本。

    另一方面:如果您要检查您的站点是否不允许SSL连接,可以在此处执行此操作(我认为这不会受到上述设置的影响,我们必须编辑注册表以强制IIS对传入连接使用TLS):网址:https://www.ssllabs.com/ssltest/index.html

    要在IIS中禁用SSL 2.0和3.0,请参阅本页:https://www.sslshopper.com/article-how-to-disable-ssl-2.0-in-IIS-7.html


    @埃迪·洛芬的回答似乎是这个问题最受欢迎的答案,但它有一些不好的长期影响。如果您在此处查看System.net.ServicePointManager.SecurityProtocol的文档页面,备注部分暗示协商阶段应该解决这一问题(强制使用协议是一种糟糕的做法,因为在将来,TLS 1.2也会受到影响)。然而,如果这个答案是真的,我们就不会去寻找它了。

    研究表明,ALPN协商协议在协商阶段需要到达TLS1.2。我们以这一点为出发点,尝试了.NET框架的新版本,以了解从何处开始支持。我们发现.NET 4.5.2不支持与TLS 1.2的协商,但.NET 4.6支持协商。

    因此,即使强制TLS1.2现在就能完成任务,我还是建议您升级到.NET 4.6。由于这是2016年6月的PCI DSS问题,窗口很短,但新框架是更好的答案。

    更新:根据评论,我构建了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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;
            }
        }

    为了验证这一概念,我将ssl3和tls1.2结合在一起,以仅支持tls 1.0和tls 1.2(禁用1.1)的服务器为目标运行代码。使用OR'D协议,它看起来连接良好。如果我更改为ssl3和tls 1.1,则连接失败。我的验证使用System.NET的httpWebRequest,只调用getResponse()。例如,我尝试了这个方法,但失败了:

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

    当这项工作开始时:

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

    这比强制TLS 1.2有一个优势,因为如果.NET框架升级到枚举中有更多条目,那么代码将支持这些条目。它比仅使用.NET 4.6有一个缺点,因为4.6使用ALPN,如果没有指定限制,它应该支持新的协议。

    编辑4/29/2019-微软去年10月发表了这篇文章。它有一个非常好的概要,介绍了如何在各种版本的.NET框架中实现这一点。


    沃森

    在Windows窗体上,它是可用的,在类的顶部

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

    因为Windows是单线程的,所以它是您所需要的,如果它是一个服务,您需要将它放在对服务调用的正上方(因为不知道您将使用哪个线程)。

    1
    using System.Security.Principal

    也是需要的。


    我必须对等价整数进行强制转换,以绕过我仍在使用.NET 4.0这一事实。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
    /* Note the property type  
       [System.Flags]
       public enum SecurityProtocolType
       {
         Ssl3 = 48,
         Tls = 192,
         Tls11 = 768,
         Tls12 = 3072,
       }
    */


    如果您想知道.NET支持哪些协议,可以在https://www.howsmysl.com上试用httpclient。/

    1
    2
    3
    4
    5
    6
    7
    // 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");

    结果糟透了:

    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.

    正如Eddie上面解释的,您可以手动启用更好的协议:

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

    我不知道为什么它使用坏的协议。这似乎是一个糟糕的设置选择,相当于一个主要的安全漏洞(我打赌很多应用程序不会改变默认设置)。我们如何报告?


    我发现最简单的解决方案是添加两个注册表项,如下所示(在具有管理员权限的命令提示符中运行此项):

    1
    2
    3
    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

    这些条目似乎会影响.NET CLR在作为客户端进行安全连接时如何选择协议。

    有关此注册表项的详细信息,请参阅:

    https://docs.microsoft.com/en-us/security-updates/securityadvisories/2015/2960358建议的操作

    这不仅简单,而且假设它适用于您的情况,远比基于代码的解决方案更健壮,后者要求开发人员跟踪协议和开发并更新所有相关代码。希望,只要.NET足够哑,不自动选择可用的最高协议,就可以对TLS 1.3及更高版本进行类似的环境更改。

    注意:尽管根据上面的文章,这只是应该禁用RC4,但是人们不会认为这会改变.NET客户端是否允许使用TLS1.2+,因为某种原因它确实具有这种效果。

    注意:正如@jordan rieger在评论中指出的,这不是poodle的解决方案,因为它不禁用旧的协议A——它只允许客户端使用新的协议,例如,当一个补丁服务器禁用了旧的协议时。然而,对于一个MITM攻击,显然一个受损的服务器将为客户机提供一个旧的协议,然后客户机将愉快地使用该协议。

    TODO:尝试对这些注册表项禁用客户端使用tls1.0和tls1.1,但是我不知道.NET HTTP客户端库是否遵守这些设置:

    https://docs.microsoft.com/en-us/windows-server/security/tls/tls注册表设置tls-10

    https://docs.microsoft.com/en-us/windows-server/security/tls/tls注册表设置tls-11