跨线程使用不同安全协议的.NET https请求

本文关键字:协议 NET https 请求 安全 线程 | 更新日期: 2023-09-27 18:25:16

我维护一个相当复杂的ASP.NET应用程序(一个自定义的NopCommerce 3.10)。在不同的场景下,它需要通过HTTPS连接到第三方服务器。我通过HttpWebRequest类执行此操作。

其中一些服务器配置不好:

其中一个第三方服务器(如ServerA)需要SSL3协议类型,如果设置了另一个协议类型,则会导致连接失败。如果使用SSL3进行连接,则另一台服务器(如服务器B)提供的证书不正确。更确切地说,它提供了一个带有错误CN(通用名称)的证书。但是,如果我从一开始就使用TLS,则证书是可以的。

我使用ServicePointManager.ServerCertificateValidationCallback回调来检查SSL策略错误,从而确定了上述问题。

更改安全协议是通过ServicePointManager.SecurityProtocol完成的,它是一个静态属性。然而,客户端对我的应用程序执行的触发上述HTTPS连接的请求可能碰巧在不同的线程中并行运行。

例如,如果我将安全协议设置为所需类型,执行HTTPS请求,然后为服务器A将其设置回,则我不能保证,如果同时有请求需要连接到Server B,则不会将ServicePointManager.SecurityProtocol更改为Server A

根据我的研究,我确定.NET没有为每个WebRequest实例提供使用特定SSL协议的方法。

我正在考虑以下解决方案:

  • 对我的应用程序中的所有传出HTTPS连接进行排队,以确保每个连接都有正确的SSL协议
  • 为每个HTTPS请求构建一个单独的应用程序域(建议https://stackoverflow.com/a/3107692/1288522)
  • 将HTTPS请求更改为低级TCP连接,并为每个连接强制执行不同的SSL协议
  • 制作一个代理asp.net应用程序,用于对传出的请求进行排队

注意:排队不会对性能造成很大影响,因为在所有客户端请求中,只有一小部分请求真正到达了有问题的代码。

然而,考虑到应用程序架构或粗略的解决方案(第三种解决方案),上述解决方案需要进行艰难的重构

我的问题与msdn上的这个问题非常相似,但没有得到令人满意的答案。

是否有更直接或有效的方法来确保每个https请求使用特定的SSL协议?

跨线程使用不同安全协议的.NET https请求

我们面临着同样的问题,并采用了您提到的应用程序域方法,基于这里提出的内容实现了一个解决方案,这是一个非常好的关于如何管理单独应用程序域中代码执行的总结:

http://www.superstarcoders.com/blogs/posts/executing-code-in-a-separate-application-domain-using-c-sharp.aspx

我们正在使用他的隔离类,基本上是这样的:

  public sealed class Isolated<T> : IDisposable where T : MarshalByRefObject
  {
    private AppDomain _domain;
    private readonly T _value;
    public Isolated()
    {
        _domain = AppDomain.CreateDomain("Isolated:" + Guid.NewGuid(), null, AppDomain.CurrentDomain.SetupInformation);
        var type = typeof(T);
        _value = (T)_domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
    }
    public T Value
    {
        get
        {
            return _value;
        }
    }
    public void Dispose()
    {
        if (_domain == null) return;
        AppDomain.Unload(_domain);
        _domain = null;
    }
}

然后我们有一个围绕标准WebClient的包装器,它允许设置协议:

public class WebClient : MarshalByRefObject, IWebClient
{
    public WebClientResponse GetResponse(string address)
    {
        return GetResponse(address, null);
    }
    public WebClientResponse GetResponse(string address, string securityProtocol)
    {
        if (!string.IsNullOrWhiteSpace(securityProtocol))
            ServicePointManager.SecurityProtocol = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), securityProtocol);
        var response = new WebClientResponse();
        try
        {
            using (var wc = new System.Net.WebClient())
            {
              // <do stuff>
            }
        }
        catch (Exception ex)
        {
            response.Exception = new GetResponseException(string.Format("Unable to get response from {0}", address), ex);
        }
        return response;
    }
}
[Serializable]
public class WebClientResponse
{
    public Exception Exception { get; set; }
    public string Response { get; set; }
}
 [Serializable]
public class GetResponseException : Exception
{
    public GetResponseException(string message, Exception innerException)
        : base(message, innerException)
    {
    }
    public GetResponseException(SerializationInfo info, StreamingContext context)  : base(info, context)
    {
    }
}

将它们连接在一起,我们有代码来确定是否需要覆盖当前设置的协议。如果是这样,它会启动隔离的应用程序域,如果不是,它会使用现有的WebClient:

...
        WebClientResponse webClientResponse;
        if (!string.IsNullOrWhiteSpace(ForceSecurityProtocol))
        {
            using (var isolated = new Isolated<WebClient>())
            {
                webClientResponse = isolated.Value.GetResponse(url, ForceSecurityProtocol);
            }
        }
        else
        {
            webClientResponse = _webClient.GetResponse(url);
        }
...

请注意,我们的使用并不是在应用程序的高吞吐量领域,所以无论我们使用这种方法付出什么性能代价,都是没有影响的。如果我们要把这样的东西放在一个阻碍我们的网络应用程序大量流量的地方,我们会做一些测试。

您可以简单地使用您提到的回调(ServicePointManager.ServerCertificateValidationCallback)来实现自定义验证逻辑:只有当错误来自恶意服务器时,才可以绕过错误。