我维护了一个非常复杂的ASP.NET应用程序(一个自定义的NopCommerce 3.10)。
它需要在不同的场景下通过HTTPS连接到第三方服务器。我是通过HttpWebRequest
类来完成的。
其中一些服务器配置不当:
其中一个第三方服务器(例如服务器A )需要 SSL3 协议类型,如果设置了其他协议类型,则只会失败连接。 如果使用SSL3执行连接,则另一台服务器(例如服务器B )提供的证书不正确。更确切地说,它提供了具有错误CN(通用名称)的证书。但是,如果我从一开始就使用 TLS ,那么证书就可以了。
我使用ServicePointManager.ServerCertificateValidationCallback
回调确定了上述问题,以检查SSL策略错误。
更改安全协议是通过ServicePointManager.SecurityProtocol完成的,这是一个静态属性。但是,客户端对我的应用程序执行的触发上述HTTPS连接的请求可能恰好在不同的线程中并行运行。
如果我,例如:将安全协议设置为所需类型,执行HTTPS请求,然后将其设置回服务器A ,我无法保证如果同时需要请求连接服务器B 不会将ServicePointManager.SecurityProtocol
更改为服务器A 所需的值以外的值。
我相信这是静态变量的典型多线程问题。
根据我的研究,我确定.NET并没有为每个WebRequest实例提供使用特定SSL协议的方法。
我正在考虑解决方案,例如:
注意:排队不会造成巨大的性能损失,因为所有客户端请求的一小部分实际上都会到达相关代码。
然而,考虑到应用程序架构或粗略的解决方法(第三种解决方案),上述解决方案需要进行困难的重构
我的问题非常相似to this one on msdn,但是没有得到满意的答案。
是否有更直接或有效的方法确保每个https请求都使用特定的SSL协议?
答案 0 :(得分:15)
我们遇到了同样的问题,并采用了您提到的app-domain方法,根据此处提出的方法实现了一个解决方案,这是一个非常好的关于如何在单独的应用程序域中管理代码执行的文章:
我们正在使用他的Isolated类:
private void text_PasswordChanged(object sender, RoutedEventArgs e)
{
if (text.Password.Length >= 8)
{
if (text.Password.ValidatePassword())
{
text.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
}
else
text.Background = new SolidColorBrush(Color.FromRgb(255, 0, 0));
}
else
text.Background = SystemColors.WindowBrush;
}
然后我们有一个标准WebClient的包装器,它允许设置协议:
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)
{
}
}
请注意,我们的使用并不是我们应用程序的极高吞吐量区域,因此无论使用此方法支付的性能价格是多么无影响。如果我们要将这样的东西放在通过我们的网络应用程序阻碍大量流量的地方,我们就会做一些测试。
答案 1 :(得分:0)
您可以简单地使用您提到的回调(ServicePointManager.ServerCertificateValidationCallback
)来实现自定义验证逻辑:只有当错误来自恶意服务器时才会绕过错误。