Catch-22防止WIF可保护的流式TCP WCF服务;破坏圣诞节,精神健康


181

我需要使用WIF保护流式WCF net.tcp服务端点。它应该根据我们的令牌服务器对传入的呼叫进行身份验证。该服务之所以流式传输是因为它旨在传输大量数据。

这似乎是不可能的。 而且如果我不能解决问题,我的圣诞节将会毁灭,我会在一个装满水槽的水里把自己喝死,而快乐的购物者会踩在我缓慢冷却的身体上。手提袋很认真,你们。

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

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

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

我说“没问题”吗?问题。实际上,NullReferenceException风格有问题。

我问框架,“兄弟,您是否还进行空检查?” 该框架是沉默的,因此我进行了分解,发现

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

是异常的来源,并且GetProperty调用正在返回null。那么,WTF?事实证明,如果我打开消息安全性并将客户端凭据类型设置为,IssuedToken则此属性现在存在于ClientFactory(提示:混蛋IChannel中没有等效的“ SetProperty”)。

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

甜。没有更多的NRE。但是,现在我的委托人出生时过错(仍然爱他,tho)。我仔细研究了WCF诊断程序(提示:让最坏的敌人在压垮他们并在他们之前开车之前,但在享受他们的女人和孩子的悲痛之前,先这样做),是因为服务器和客户端之间的安全性不匹配。

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

检查主机的诊断(再次:暗恋,开车,阅读日志,感叹),我认为这是对的

协议类型application / ssl-tls已发送到不支持该类型的升级的服务。

我说:“好吧,自我,我将打开主机上的消息安全性!” 我知道 如果您想知道它的外观,那是客户端配置的精确副本。抬头。

结果: Kaboom。

绑定('NetTcpBinding',' http://tempuri.org/ ')支持无法与消息级别安全性一起配置的流。考虑选择其他传输模式或选择传输级别的安全性。

因此,不能通过令牌同时托管和托管我的主机。赶上22。

tl;博士:如何使用WIF保护流式net.tcp WCF端点?


3
好的,这里可能是无知的问题,但是WIF真的需要消息模式吗?传输模式听起来更适合流媒体播放,例如未经测试的内容<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
Joachim Isaksson

3
TransportWithMessageCredential模式可能是另一种选择。
约阿希姆·伊萨克森

3
TMLK,MessageSecurity可以对缓冲的有效负载进行签名和加密,但是在处理流时会失败。您是否考虑过使用authenticationMode = IssuedTokenOverTransport?
OnoSendai

7
让我看看我是否可以召集一些过去的鬼魂来帮助您节省假期。这里的一些提示:social.msdn.microsoft.com/Forums/vstudio/en-US/...
OnoSendai

2
您是否有机会发布其他人可以尝试的测试用例项目?
antiduh

Answers:


41

WCF在流媒体处理方面存在一些问题(我正在看您,MTOM 1),这是由于它无法执行大多数人认为应该起作用的方式进行预身份验证的根本问题(它只会影响对该频道的后续请求) ,而不是第一个请求)好吧,所以这不完全是您的问题,但请继续,因为我将在最后解决您的问题。通常,HTTP挑战的工作方式如下:

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

现在,如果您曾经尝试在服务器上的WCF终结点上启用MTOM流,它将不会抱怨。但是,当您在客户端代理上配置它时(如您所愿,它们必须匹配绑定),它会爆炸而死。其原因是WCF试图阻止的上述事件序列是:

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

注意,当您只需要发送100MB时,您刚刚向服务器发送了200MB。好吧,这就是问题所在。答案是在第一次尝试时发送身份验证,但是如果没有编写自定义行为,则在WCF中是不可能的。无论如何,我离题了。

你的问题

首先,让我告诉您您要尝试的事情是不可能2。现在,为了让您停止旋转轮子,让我告诉您原因:

令您惊讶的是,您现在正在徘徊在类似的问题中。如果启用消息级安全性,则客户端必须先将整个数据流加载到内存中,然后才能使用ws-security所需的常规哈希函数和xml签名实际关闭消息。如果必须读取整个流以对单个消息进行签名(这不是真正的消息,而是单个连续流),那么您可以在此处看到问题。WCF必须“本地”传输一次以计算消息安全性,然后再次传输它以将其发送到服务器。这显然是一件愚蠢的事情,因此WCF不允许流数据的消息级安全性。

因此,这里的简单答案是,您应该将令牌作为参数发送给初始Web服务,或者作为SOAP标头发送,并使用自定义行为对其进行验证。您不能使用WS-Security来做到这一点。坦白地说,这不仅仅是WCF问题-我看不到它在其他任何堆栈中实际上如何工作。

解决MTOM问题

这仅是一个示例,说明我如何解决基本身份验证的MTOM流问题,因此也许您可以胆量大一些,并为您的问题实施类似的操作。症结在于,为了启用您的自定义消息检查器,除了传输级别(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

请注意,我在这里关闭了传输安全性,因为我将使用消息检查器和自定义行为来提供自己的信息:

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问题困扰的任何人,同时也可以作为您实施类似于对由主要WIF保护的令牌服务生成的令牌进行身份验证的框架。

希望这可以帮助。

(1)大数据和流

(2)WCF中的消息安全性(请参阅“缺点”)。


MTOM and Basic Authorization,以及MTOM和OAuth2
Kiquenet
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.