小编典典

Catch-22 阻止 WIF 保护的流式 TCP WCF 服务;毁了我的圣诞节,心理健康

all

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

这似乎是不可能的。 如果我不能绕开这个问题,我的圣诞节就会毁了,我会在阴沟里喝死,而快乐的购物者会踩到我慢慢冷却的身体。托特严重,你们。

为什么这是不可能的?这是第 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(protip:在
IChannel 中没有“SetProperty”等价物,混蛋)。

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

甜的。没有更多的 NRE。然而,现在我的客户 在出生时就有缺陷 (仍然爱他,寿)。深入研究 WCF
诊断(提示:让你最坏的敌人在粉碎他们并将他们赶到你面前,但在享受他们的妇女和孩子的悲痛之前这样做),我认为这是因为服务器和客户端之间的安全不匹配。

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

检查主机的诊断(再次:迷恋,开车,阅读日志,享受感叹),我看到这是真的

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

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

结果: 卡布姆。

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

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

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


阅读 78

收藏
2022-08-02

共1个答案

小编典典

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 中的消息安全(参见“缺点”)

2022-08-02