【问题标题】:Disable SSL fallback and use only TLS for outbound connections in .NET? (Poodle mitigation)禁用 SSL 回退并仅将 TLS 用于 .NET 中的出站连接? (贵宾犬缓解)
【发布时间】:2014-12-10 23:08:13
【问题描述】:

我正在尝试减轻我们对Poodle SSL 3.0 Fallback 攻击的脆弱性。我们的管理员已经开始禁用 SSL 以支持 TLS 用于与我们服务器的入站连接。我们还建议我们的团队在他们的网络浏览器中禁用 SSL。我现在正在查看我们的 .NET 代码库,它通过 System.Net.HttpWebRequest 启动与各种服务的 HTTPS 连接。我相信如果这些连接允许从 TLS 回退到 SSL,它们可能容易受到 MITM 攻击。这是我到目前为止所确定的。有人可以仔细检查一下以确认我是对的吗?这个漏洞是全新的,所以我还没有看到 Microsoft 提供的关于如何在 .NET 中缓解它的任何指导:

  1. System.Net.Security.SslStream 类的允许协议支持 .NET 中的安全通信,通过 System.Net.ServicePointManager.SecurityProtocol 为每个 AppDomain 全局设置财产。

  2. .NET 4.5 中此属性的默认值为Ssl3 | Tls(虽然我找不到支持它的文档。)SecurityProtocolType 是一个带有 Flags 属性的枚举,所以它是按位的 OR em> 这两个值。您可以使用这行代码在您的环境中进行检查:

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

  3. 在您在应用中启动任何连接之前,应将其更改为 Tls,或者可能是 Tls12

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

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

TLS 1.0 与 1.1/1.2 的更新:

根据 Google 安全专家 Adam LangleyTLS 1.0 was later found to be vulnerable to POODLE if not implemented correctly 的说法,因此您应该考虑完全迁移到 TLS 1.2。

.NET Framework 4.7 及更高版本更新:

正如下面Prof Von Lemongargle 所提到的,从 .NET Framework 4.7 版开始,无需使用此 hack,因为默认设置将允许操作系统选择最安全的 TLS 协议版本。请参阅Transport Layer Security (TLS) best practices with the .NET Framework 了解更多信息。

【问题讨论】:

  • 关于以上第 2 点:见 referencesource.microsoft.com/#System/net/System/Net/… SslProtocols " Default = Ssl3 | Tls"
  • @Dai Bok 现在默认是SystemDefault选项docs.microsoft.com/en-us/dotnet/api/…
  • @MarwaAhmad 如果是这种情况,我的链接中的源代码参考不反映默认值 (48 | 192) 。如果设置为 none ( 0 ) ,它应该恢复为系统默认值。这对我来说很臭,我可能会在进行更改之前对其进行测试,因为它可能会导致配置错误...并关闭错误 .net 框架上的协议...

标签: .net ssl


【解决方案1】:

我们正在做同样的事情。要仅支持 TLS 1.2 而不支持 SSL 协议,您可以这样做:

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

【讨论】:

  • 对。支持较新的 TLS 版本的好主意:面向未来。
  • 为了他人的利益,您提到的注册表编辑也可以通过以下步骤完成:serverfault.com/a/637263/98656。在 Windows Server 2003 到 2012 R2 中,协议由 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols 中的标志控制。要禁用 SSLv3,请在上述位置创建一个名为“SSL 3.0”的子项,在该位置下创建一个名为“Server”的子项,并在该位置下创建一个名为“Enabled”的 DWORD 值,设置为 0。您还应该禁用 SSL 2.0以同样的方式。
  • @ScotterMonkey 如果您从 .NET 代码启动 出站 连接,您只需设置 System.Net.ServicePointManager.SecurityProtocol,例如从服务器上运行的自定义代码连接到 Web 服务或 API。如果您只运行一个简单的网站,并且只接受来自浏览器的传入连接,那么注册表修复就足够了。
  • 注意:SecurityProtocolType.Tls11SecurityProtocolType.Tls12 枚举值似乎仅在 ASP.net 4.5 及更高版本中可用。不确定如果 TLS 1.0 被淘汰,我们将如何处理在 2.0 上运行的旧代码库。
  • @AnishV 你把那行代码放在你的应用程序的初始化中,在任何启动出站 SSL/TLS 连接的代码之前。
【解决方案2】:

@Eddie Loeffen 的回答似乎是这个问题最受欢迎的回答,但它有一些不好的长期影响。如果您查看 System.Net.ServicePointManager.SecurityProtocol here 的文档页面,则备注部分暗示协商阶段应该解决这个问题(并且强制协议是不好的做法,因为将来,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 问题,因此窗口期很短,但新框架是更好的答案。

更新: 从 cmets 工作,我构建了这个:

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()。例如,我试过这个并失败了:

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

虽然这有效:

        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 框架以使 Enum 中有更多条目,那么代码将按原样支持它们。与仅使用 .Net 4.6 相比,它的缺点是 4.6 使用 ALPN,如果未指定限制,则应支持新协议。

编辑 2019 年 4 月 29 日 - Microsoft 去年 10 月发布了 this article。它很好地概括了他们关于如何在各种版本的 .net 框架中完成此操作的建议。

【讨论】:

  • 有趣。看起来文档页面随着 MSDN 移动到“.NET Framework (当前版本)”而更新,现在解释说“故意没有为此属性列出默认值”。也许接受答案的更具前瞻性的版本是首先查询属性以检索允许的协议列表,然后删除您的应用程序认为不安全的协议(即低于 TLS 1.2 的协议),而不是仅显式添加你认为安全的那些。这不会排除未来的新版本。
  • 能够让程序从系统生成的列表中删除协议会很好,但我在当前的 API 中看不到这样做的方法。唯一的选择似乎是强制执行特定协议,我不明白为什么有人愿意这样做。不幸的是,如果没有 ALPN 支持,这似乎是使 TLS1.2 连接正常工作的唯一方法。
  • 应该可以遍历所有 SecurityProtocolType 枚举,查看 ServicePointManager.SecurityProtocol flags 枚举中存在哪些(这是所有设置标志的逻辑 OR,因此您可以使用AND),然后建立一个新的列表,其中包含您不想删除的列表。然后将它们组合成一个枚举并用它设置属性。
  • 我只是在重新阅读您的枚举/循环代码,我认为这是不正确的。 |= 逻辑运算符将导致属性包括最初在属性中设置的任何默认值,而不是仅包括 Tls12 及更高版本。要修复它,您必须初始化一个值为 0 的 SecurityProtocolType 枚举变量,对该变量执行 |= 循环,然后将其分配给 ServicePointManager.SecurityProtocol 属性。
  • 有没有人觉得 .NET 不能自动协商它能够处理的最高协议版本很疯狂?!
【解决方案3】:

我不得不转换整数等价物来解决我仍在使用 .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,
   } 
*/

【讨论】:

  • 这可行,但 .NET 4.0 不支持 TLS 1.2,但 .NET 4.5 及更高版本支持 TLS 1.2。因此,您可以将此代码(或添加按位 OR 以添加对 TLS 1.2 协商的支持)添加到您的 .NET 4.0 应用程序并构建它,但您需要在 .NET 4.5 或更高版本上部署代码。 blogs.perficient.com/microsoft/2016/04/tsl-1-2-and-net-support
  • 另请参阅,.NET 4.0 代码可以在更高版本的 .NET 上正常工作,包括 .NET 4.5 和 .NET 4.6 stackoverflow.com/questions/33378902/…
  • 整数转换适用于发送 TLS 1.2。 .NET 4.0 中不存在 Tls12 枚举值
  • 两个不同的点。看我的评论。您可以输入该值,但如果您的运行时网络版本是 4.0,它将失败。您可以使用此标志进行编译,但您的运行时 .NET 版本需要为 4.5 或更高版本,因为 .NET 4.0 上的基本组件不支持 TLS 1.2 所需的密码(请参阅链接)
  • 对于较旧的 .NET 运行时版本有几个修补程序以添加 TLS 1.2 支持。例如support.microsoft.com/en-us/help/3154518/…,CZahrobsky 也可以在 Win10 秋季创作者更新中使用它,其中还包括修补程序。
【解决方案4】:

@watson

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

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

因为 windows 是单线程的,所以你只需要它,如果它是一个服务,你需要把它放在对服务的调用之上(因为不知道你会在哪个线程上)。

using System.Security.Principal 

也是需要的。

【讨论】:

    【解决方案5】:

    如果您对 .NET 支持哪些协议感到好奇,可以在 https://www.howsmyssl.com/ 上试用 HttpClient

    // 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");
    

    结果很糟糕:

    您的客户端使用的是非常旧的 TLS 1.0,可能容易受到 BEAST 攻击,并且没有可用的最佳密码套件。 TLS 1.0 客户端以及更多现代密码套件无法使用 AES-GCM 和 SHA256 等替代 MD5-SHA-1 的附加功能。

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

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

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

    【讨论】:

      【解决方案6】:

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

      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#suggested-actions

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

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

      注意:正如@Jordan Rieger 在 cmets 中所指出的,这不是 POODLE 的解决方案,因为它不会禁用旧协议 a - 它只是允许客户端使用较新的协议,例如当打补丁的服务器禁用了旧协议时。但是,对于 MITM 攻击,很明显,受感染的服务器会为客户端提供旧协议,然后客户端会愉快地使用该协议。

      TODO:尝试通过这些注册表项禁用客户端对 TLS1.0 和 TLS1.1 的使用,但是我不知道 .NET http 客户端库是否尊重这些设置:

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

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

      【讨论】:

      • 将注册表设置作为代码的替代方案会很棒,但是这些设置是否实际上禁用了 TLS 1.0 和 1.1,而只允许使用 TLS 1.2 及更高版本的客户端连接?根据链接,它似乎只在 TLS 中禁用 RC4。我认为贵宾犬的攻击范围比这更广泛。
      • @JordanRieger 这些注册表项允许 .NET 客户端连接到禁用旧协议以缓解 POODLE 的服务器。没有这些,客户端将抛出错误,因为 .NET 客户端愚蠢地坚持使用旧协议,即使服务器要求使用新协议。如果您的服务器仍然允许使用旧协议进行连接,那么所有的赌注都将被取消。理论上旧的协议应该被禁用。我想知道允许在服务器上禁用这些的相同注册表项(例如nartac.com/Products/IISCrypto)是否也适用于客户端?
      • 在 nartac 操作的注册表项中,(作为)客户端和(作为)服务器有单独的设置。我不记得他们的界面是否有区别。你知道什么版本的 .net 支持这个注册表黑客吗?
      • @Raman 我的问题的重点是当您的客户端连接到您无法控制的服务器时缓解 POODLE 漏洞。该漏洞将允许 MITM 攻击者将协议降级到可以被黑客入侵的旧 TLS 或 SSL 版本。仅仅禁用 RC4 是不够的。这些注册表设置可能对某些情况有用,但对我的问题中的情况没有用。
      • @JordanRieger 同意。我用一些额外的观察和想法更新了文本。
      猜你喜欢
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-24
      • 2014-12-27
      • 1970-01-01
      • 2016-02-28
      • 1970-01-01
      相关资源
      最近更新 更多