2014-10-02 45 views
14

我维护一个相当复杂的ASP.NET应用程序(一个自定义的NopCommerce 3.10)。 它需要通过HTTPS在不同的场景下连接到第三方服务器。我通过HttpWebRequest这个课程来完成这个任务。NET HTTPS请求与跨线程的不同安全协议

一些服务器都配置不当:

一个第三方的服务器(比如服务器A)要求SSL3协议类型,如果其他协议类型设置只是失败的连接。 如果使用SSL3执行连接,则另一台服务器(如服务器B)会提供不正确的证书。更确切地说,它提供了一个错误的CN(通用名称)的证书。但是,如果我从一开始就使用TLS,则证书是确定的。

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

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

如果我,例如:安全协议设置为所需的类型,执行HTTPS请求,然后将其设置回为服务器A,我没有保证,如果在此期间的请求需要连接到服务器B不会将ServicePointManager.SecurityProtocol更改为除服务器A所需的值之外的值。 我认为这是一个典型的多线程问题与静态变量。

从我的研究中,我确定.NET并未提供为每个WebRequest实例使用特定SSL协议的意思。

我在考虑解决方案,如:

  • 排队我的应用程序内的所有传出的HTTPS连接,以确保每一个
  • 构建一个单独的应用程序域为每个HTTPS请求正确的SSL协议(建议通过https://stackoverflow.com/a/3107692/1288522
  • 变化HTTPS请求到低级别的TCP连接,并执行为每个
  • 的不同SSL协议作出代理asp.net应用程序,这将排队传出请求

注意:排队不会是一个巨大的性能打击,因为所有客户端请求的一小部分实际上到达了相关代码。

上述解决方案,但是,需要重构很难考虑到应用基础架构或粗糙的解决方法(第三溶液)

我的问题是非常相似to this one on msdn,但是这一次没有得到满意答复。

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

+0

你有没有深入到底?我们似乎有发送代码HTTP请求使用TLS 1.0协议 – 2015-04-01 10:36:45

+0

@TomMiller我从来没有使用实现每个请求的不同的安全协议的解决方案的一部分我们自己的RESTful API类似的问题,因为所有SSL3相关的代码移除或由于“卷毛狗”漏洞而被阻挡。有点巧合的是,这个漏洞在我的问题两周后公开披露。我当时想到的可能解决方案清单将是一个起点。 – 2015-04-01 14:25:58

回答

11

我们面临着同样的问题,并就你提到的应用程序域方法,实施基于这里提出什么样的解决方案,这是如何管理一个单独的应用程序的代码执行一个非常好的写了域:

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; 
    } 
} 

然后我们身边的标准Web客户端的包装,即允许协议的设置:

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) 
    { 
    } 
} 

将它们绑在一起,我们有确定是否需要重写当前设置的协议的代码。如果是这样,它旋转了隔离应用程序域,如果不是它使用现有的Web客户端:

... 
     WebClientResponse webClientResponse; 

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

注意,我们使用的是不是在我们的应用程序的一个非常高通量的区域,所以无论性能价格我们使用这种方法付费真的没有影响力。如果我们打算把这样的事情在它一直在通过我们的web应用程序流量的显著量的方式的地方,我们会做一些测试。

+0

对不起,如果这是一个愚蠢的问题 - 我在哪里可以找到IWebClient的定义? – 2016-06-17 22:52:02

+0

@NathanaelSchulte这只是与WebClient类的公共方法使用该Web客户端的代码是可测试的独立的接口,使用如此。 – Veatch 2016-06-20 14:41:31

0

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