首页 文章

Catch-22防止WIF保证流式TCP WCF服务;毁了我的圣诞节,心理 Health

提问于
浏览
180

我有 secure a streamed WCF net.tcp service endpoint using WIF 的要求 . 它应该对我们的令牌服务器验证传入呼叫 . 该服务是流式传输的,因为它旨在传输大量数据 .

This appears to be impossible. 如果我能在阴沟里喝酒,快乐的购物者会踩到我缓慢冷却的身体 . 手提箱严重,你们 .

为什么这不可能?这是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();

我说"no problemo"了吗? Problemo . 事实上, NullReferenceException 风格的问题 .

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

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

是异常的来源, GetProperty 调用返回 null . 那么,WTF?事实证明,如果我打开Message security并将客户端凭证类型设置为 IssuedToken ,则此属性现在存在于 ClientFactory 中(protip:IChannel中没有"SetProperty"等效,即混蛋) .

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

甜 . 没有更多的NRE . 然而,现在我的客户在出生时出现了故障(仍然爱他,所以) . 通过WCF诊断进行挖掘(protip:让你的最坏的敌人在击碎它们并在你面前驾驶它们之前做到这一点,但在享受他们的女人和孩子的悲伤之前),我看到它是因为服务器和客户端之间的安全性不匹配 .

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

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

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

"Well, self,"我说,"I'll just turn on Message security on the host!"而且我这样做 . 如果你想知道它的样子,它就是客户端配置的精确副本 . 抬头 .

结果: Kaboom.

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

所以, my host cannot be both streamed and secured via tokens . 第二十二条军规 .

tl;dr: How can I secure a streamed net.tcp WCF endpoint using WIF???

1 回答

  • 41

    WCF在一些领域有流媒体(我正在看你,MTOM1),因为它无法按照大多数人认为应该工作的方式执行预身份验证的基本问题(它只会影响该 Channels 的后续请求,不是第一个请求)好的,所以这不是你的问题,但请跟进,因为我会在最后找到你的 . 通常,HTTP挑战的工作方式如下:

    • 客户端匿名命中服务器

    • 服务器说,抱歉,401,我需要身份验证

    • 客户端使用身份验证令牌命中服务器

    • 服务器接受 .

    现在,如果您尝试在服务器上的WCF endpoints 上启用MTOM流,则不会抱怨 . 但是,当你在客户端代理上配置它时(你应该,它们必须匹配绑定)它会在火热的死亡中爆炸 . 原因是WCF试图阻止的上述事件序列是这样的:

    • 客户端在一个POST中匿名将100MB文件流式传输到服务器

    • 服务器说抱歉,401,我需要身份验证

    • 客户端再次使用身份验证标头将100MB文件流式传输到服务器

    • 服务器接受 .

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

    Your Problem

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

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

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

    Solving the MTOM Problem

    这只是一个例子,我如何解决我的基本身份验证的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)Large Data and Streaming

    (2)Message Security in WCF(见"disadvantages.")

相关问题