Catch-22防止通过WIF保证stream式TCP WCF服务的安全 毁了我的圣诞节,心理健康

我需要使用WIF保护stream式WCF net.tcp服务端点 。 它应该authentication来自我们的令牌服务器的来电。 该服务是stream式传输,因为它旨在传输大量数据。

这似乎是不可能的。 如果我无法绕过捕捉,我的圣诞节将被毁坏,我会喝水自己死在一个天沟,而快乐的购物者走过我慢慢冷却的身体。 大家好,你们。

为什么这是不可能的? 这是Catch-22。

在客户端上,我需要使用从令牌服务器获得的GenericXmlSecurityToken创build一个通道。 没问题。

// people around here hate the Framework Design Guidelines. var token = Authentication.Current._Token; var service = base.ChannelFactory.CreateChannelWithIssuedToken(token); return service.Derp(); 

我说“没问题”吗? Problemo。 实际上, NullReferenceException样式问题。

“兄弟,”我问框架,“你甚至无效检查? 该框架是沉默的,所以我拆解,发现

 ((IChannel)(object)tChannel). GetProperty<ChannelParameterCollection>(). Add(federatedClientCredentialsParameter); 

是exception的来源,并且GetProperty调用返回null 。 那么,WTF? 原来,如果我打开消息安全性并将客户端凭证types设置为IssuedToken则此属性现在存在于ClientFactory (protip:在IChannel中没有“SetProperty”等价物,这个混蛋)。

 <binding name="OMGWTFLOL22" transferMode="Streamed" > <security mode="Message"> <message clientCredentialType="IssuedToken"/> </security> </binding> 

甜。 没有更多的NREs。 但是,现在我的客户出生时出现了故障 (仍然爱他,寿)。 通过WCF诊断(protip:让你最糟糕的敌人在粉碎它们之前做这些事情,然后在享受他们的女人和孩子们的哀叹之前驾驶它们),我看到这是因为服务器和客户端之间的安全不匹配。

“net.tcp:// localhost:49627 / MyService”不支持所请求的升级。 这可能是由于绑定不匹配(例如,在客户端而不是在服务器上启用了安全性)。

检查主机的诊断(再次:粉碎,驱动器,读取日志,享受哀叹),我看到这是真的

协议typesapplication / ssl-tls被发送到不支持该types升级的服务。

“好吧,我自己,”我说,“我只要打开主机上的消息安全!” 而我呢。 如果你想知道它是什么样子的,这是客户端configuration的一个确切的副本。 抬头。

结果: Kaboom。

绑定('NetTcpBinding',' http://tempuri.org/ ')支持不能与消息级安全一起configuration的stream。 考虑select不同的传输模式或select传输级别的安全性。

所以, 我的主机不能通过令牌stream式传输和保护 。 第二十二条军规。

tl; dr:如何使用WIF保护stream式net.tcp WCF端点?

由于一个基本问题,WCF在一些有stream媒体的领域(我正在看你,MTOM 1 )有一些基本问题,这是大多数人认为应该工作的方式不能执行预authentication的原因(它只影响对该频道的后续请求,而不是第一个请求)好的,所以这不是你的问题,但请继续,因为我会在最后得到你的问题。 通常,HTTP挑战是这样的:

  1. 客户端匿名访问服务器
  2. 服务器说,对不起,401,我需要authentication
  3. 客户端用身份validation令牌访问服务器
  4. 服务器接受。

现在,如果您尝试在服务器上的WCF端点上启用MTOMstream式传输,则不会发生抱怨。 但是,当你在客户端代理上进行configuration时(它们必须与绑定相匹配),它将在火热的死亡中爆炸。 原因是WCF试图防止的上述事件序列是这样的:

  1. 客户端在单个POST中匿名传输100MB文件到服务器
  2. 服务器说对不起,401,我需要authentication
  3. 客户端再次使用身份validation头将100MB文件传输到服务器
  4. 服务器接受。

注意,当你只需要发送100MB的数据时,你只发送了200MB到服务器。 那么,这是问题。 答案是在第一次尝试时发送身份validation,但在WCF中这是不可能的,无需编写自定义行为。 无论如何,我离题了。

你的问题

首先,让我告诉你,你正在尝试的是不可能的2 。 现在,为了让你停止旋转你的轮子,让我告诉你为什么:

我觉得你现在在类似的问题上徘徊。 如果启用消息级安全性,则客户端必须将整个数据stream加载到内存中,然后才能使用ws-security所需的常规哈希函数和xml签名来实际closures消息。 如果它必须读取整个stream来签署单个消息(这不是一个真正的消息,但它是一个单一的连续stream),那么你可以在这里看到问题。 WCF将不得不一次“本地”stream式传输,以计算消息安全性,然后再次stream式传输到服务器。 这显然是一件愚蠢的事情,所以WCF不允许stream数据的消息级安全性。

所以,这里的简单答案是,您应该将标记作为参数发送到初始Web服务,或者作为SOAP标头,并使用自定义行为来validation它。 您不能使用WS-Security来执行此操作。 坦率地说,这不仅仅是一个WCF的问题 – 我不明白它是如何实际上为任何其他堆栈工作。

解决MTOM问题

这只是一个例子,我如何解决了我的MTOMstream媒体问题的基本authentication,所以也许你可以采取这个胆量和实施类似的问题。 关键在于,为了启用自定义消息检查器,除了传输级别(SSL)之外,还必须禁用客户机代理上的所有安全性概念(在服务器上保持启用状态):

 this._contentService.Endpoint.Behaviors.Add( new BasicAuthenticationBehavior( username: this.Settings.HttpUser, password: this.Settings.HttpPass)); var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding; binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; // Do not provide 

请注意,我已经closures了传输安全性,因为我将使用消息检查器和自定义行为自己提供:

 internal class BasicAuthenticationBehavior : IEndpointBehavior { private readonly string _username; private readonly string _password; public BasicAuthenticationBehavior(string username, string password) { this._username = username; this._password = password; } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { var inspector = new BasicAuthenticationInspector( this._username, this._password); clientRuntime.MessageInspectors.Add(inspector); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } } internal class BasicAuthenticationInspector : IClientMessageInspector { private readonly string _username; private readonly string _password; public BasicAuthenticationInspector(string username, string password) { this._username = username; this._password = password; } public void AfterReceiveReply(ref Message reply, object correlationState) { } public object BeforeSendRequest(ref Message request, IClientChannel channel) { // we add the headers manually rather than using credentials // due to proxying issues, and with the 101-continue http verb var authInfo = Convert.ToBase64String( Encoding.Default.GetBytes(this._username + ":" + this._password)); var messageProperty = new HttpRequestMessageProperty(); messageProperty.Headers.Add("Authorization", "Basic " + authInfo); request.Properties[HttpRequestMessageProperty.Name] = messageProperty; return null; } } 

因此,这个示例适用于任何正在遭遇MTOM问题的人,也可以作为一个框架来实现类似的function,以validation由主WIF安全的令牌服务生成的令牌。

希望这可以帮助。

(1) 大数据和stream媒体

(2) WCF中的消息安全 (见“缺点”)