关于c#:Catch-22阻止了WIF保护的流式TCP WCF服务;

Catch-22 prevents streamed TCP WCF service securable by WIF; ruining my Christmas, mental health

我需要使用wif保护流式wcf net.tcp服务端点。它应该针对我们的令牌服务器对传入的呼叫进行身份验证。这项服务之所以被流化,是因为它的设计目的是传输大量的数据。

这似乎是不可能的。如果我不能避开这个陷阱,我的圣诞节将被毁掉,我会在阴沟里喝死自己,而快乐的购物者会跨过我慢慢冷却的身体。我是认真的,伙计们。

为什么这是不可能的?这是22号门。

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

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

我说"没问题"了吗?问题。实际上,NullReferenceException样式的问题。

"兄弟,"我问框架,"你连空检查都没有吗?"框架是无声的,所以我拆开发现

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

是异常的来源,GetProperty调用返回null。那么,WTF?结果发现,如果我打开消息安全并将客户机凭证类型设置为IssuedToken,那么这个属性现在就存在于ClientFactory中(protip:在混蛋ichannel中没有等价的"set property")。

1
2
3
4
5
<binding name="OMGWTFLOL22" transferMode="Streamed">
    <security mode="Message">
        <message clientCredentialType="IssuedToken"/>
    </security>
</binding>

甜的。没有更多的NRIs。然而,现在我的客户在出生时就有过错(仍然爱他,透)。通过对WCF诊断的深入研究(Protip:让你最坏的敌人在碾碎他们并在你面前驾驶他们之后,但在享受他们妇女和孩子的哀悼之前),我发现这是因为服务器和客户机之间的安全不匹配。

The requested upgrade is not supported by 'net.tcp://localhost:49627/MyService'. This could be due to mismatched bindings (for example security enabled on the client and not on the server).

检查主机的诊断(再次:挤压、驾驶、阅读日志、享受悲伤),我发现这是真的。

Protocol Type application/ssl-tls was sent to a service that does not support that type of upgrade.

"好吧,我自己,"我说,"我就打开主机上的消息安全!"我也是。如果您想知道它是什么样子的,它是客户机配置的精确副本。抬头看,

结果:Kaboom。

The binding ('NetTcpBinding','http://tempuri.org/') supports streaming which cannot be configured together with message level security. Consider choosing a different transfer mode or choosing the transport level security.

因此,我的主机不能同时通过令牌进行流式传输和安全保护。第二十二条军规。

tl;dr:如何使用wif保护流式net.tcp wcf端点????


WCF在流媒体的一些领域(我在看你,MTOM1),由于一个基本问题,即它如何不能像大多数人认为的那样执行预身份验证(它只影响对该频道的后续请求,而不是第一个请求),所以这不是你的问题,但请按照我将得到的来做到最后你的。通常,HTTP质询的工作方式如下:

  • 客户端匿名访问服务器
  • 服务器说,对不起,401,我需要身份验证
  • 客户端使用身份验证令牌访问服务器
  • 服务器接受。
  • 现在,如果您尝试在服务器上的WCF端点上启用MTOM流,它将不会抱怨。但是,当您在客户机代理服务器上配置它时(正如您应该配置的那样,它们必须匹配绑定),它将在激烈的死亡中爆炸。原因是WCF试图阻止的上述事件序列是:

  • 客户端在单个日志中将100MB文件匿名传输到服务器
  • 服务器说对不起,401,我需要身份验证
  • 客户端再次使用身份验证头将100MB文件流到服务器
  • 服务器接受。
  • 注意,当只需要发送100MB时,您刚刚向服务器发送了200MB。这就是问题所在。答案是在第一次尝试时发送身份验证,但如果不编写自定义行为,在WCF中是不可能的。不管怎样,我离题了。

    你的问题

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

    我突然想到,你现在正徘徊在一个类似的问题中。如果启用消息级安全性,则客户端必须先将整个数据流加载到内存中,然后才能使用WS-Security所需的常规哈希函数和XML签名实际关闭消息。如果它必须读取整个流来签署单个消息(这不是真正的消息,但它是一个连续的流),那么您可以在这里看到问题。WCF必须在"本地"流一次以计算消息安全性,然后再次流一次以将其发送到服务器。这显然是一个愚蠢的事情,所以WCF不允许流数据的消息级安全性。

    因此,这里的简单答案是,您应该将令牌作为参数发送到初始Web服务,或者作为SOAP头发送,并使用自定义行为来验证它。不能使用WS-Security执行此操作。坦率地说,这不仅仅是一个WCF问题——我看不出它实际上如何适用于任何其他堆栈。

    解决MTOM问题

    这只是一个示例,我如何解决基本身份验证的MTOM流问题,所以也许您可以利用这一点,为您的问题实现类似的东西。其关键在于,为了启用自定义消息检查器,除了传输级别(SSL)之外,必须禁用客户端代理上的所有安全概念(它在服务器上保持启用状态):

    1
    2
    3
    4
    5
    6
    7
    8
    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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    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中的消息安全性(参见"缺点")。