我正在尝试减轻我们对 Poodle SSL 3.0 Fallback 攻击的脆弱性。我们的管理员已经开始禁用 SSL 以支持 TLS 用于与我们服务器的入站连接。我们还建议我们的团队在他们的网络浏览器中禁用 SSL。我现在正在查看我们的 .NET 代码库,它通过 System.Net.HttpWebRequest 启动与各种服务的 HTTPS 连接。我相信如果这些连接允许从 TLS 回退到 SSL,它们可能容易受到 MITM 攻击。这是我到目前为止所确定的。有人可以仔细检查一下以确认我是对的吗?这个漏洞是全新的,所以我还没有看到微软关于如何在 .NET 中缓解它的任何指导:
System.Net.Security.SslStream 类的允许协议支持 .NET 中的安全通信,通过 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 的更新:
根据 Google 安全专家 Adam Langley TLS 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。
我们正在做同样的事情。要仅支持 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
@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 问题,因此窗口很短,但新框架是一个更好的答案。
更新:根据评论,我构建了这个:
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 框架中完成这项工作的建议。
我不得不转换整数等价物来解决我仍在使用 .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,
}
*/
@沃森
在 Windows 窗体上它是可用的,放在类的顶部
static void Main(string[] args)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//other stuff here
}
因为windows是单线程的,所以你只需要它,如果它是一个服务,你需要把它放在对服务的调用之上(因为不知道你会在哪个线程上)。
using System.Security.Principal
也是需要的。
如果您对 .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;
我不知道为什么它使用开箱即用的坏协议。这似乎是一个糟糕的设置选择,相当于一个重大的安全漏洞(我敢打赌,很多应用程序都不会更改默认设置)。我们如何举报?
我发现最简单的解决方案是添加两个注册表项,如下所示(在具有管理员权限的命令提示符下运行):
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 在评论中所指出的,这不是 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
SecurityProtocolType.Tls11
和SecurityProtocolType.Tls12
枚举值仅在 ASP.net 4.5 及更高版本中可用。如果 TLS 1.0 被搁置,我们不确定对于在 2.0 上运行的旧代码库要做什么。