O Catch-22 impede que o serviço TCP WCF transmitido seja protegido por WIF; arruinando meu Natal, saúde mental


181

Tenho um requisito para proteger um ponto de extremidade do serviço net.tcp do WCF transmitido usando WIF . Ele deve autenticar as chamadas recebidas no nosso servidor de token. O serviço é transmitido porque foi projetado para transferir grandes quantidades de dados e coisas.

Isso parece ser impossível. E se eu não conseguir contornar o problema, meu Natal será arruinado e eu me matarei até a sarjeta enquanto compradores alegres passam por cima do meu corpo lentamente esfriando. É sério, pessoal.

Por que isso é impossível? Aqui está o Catch-22.

No cliente, preciso criar um canal com o GenericXmlSecurityToken que recebo do nosso servidor de token. Não há problema.

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

Eu disse "no problemo"? Problemo. De fato, NullReferenceExceptionestilo problemo.

"Bro", perguntei ao Framework, "você checa mesmo?" O Framework ficou em silêncio, então eu desmontei e descobri que

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

foi a fonte da exceção e que a GetPropertychamada estava retornando null. Então, WTF? Acontece que, se eu ativar a Segurança da mensagem e definir o tipo de credencial do cliente IssuedToken, essa propriedade agora existe no ClientFactory(protip: Não há "SetProperty" equivalente no IChannel, o bastardo).

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

Doce. Não há mais NREs. No entanto, agora meu cliente é culpado de nascença (ainda o amo). Indo além dos diagnósticos do WCF (protip: faça seus piores inimigos fazer isso depois de esmagá-los e levá-los diante de você, mas antes de apreciar as lamentações de mulheres e crianças), vejo que é por causa de uma incompatibilidade de segurança entre o servidor e o cliente.

A atualização solicitada não é suportada por 'net.tcp: // localhost: 49627 / MyService'. Isso pode ocorrer devido a ligações incompatíveis (por exemplo, segurança ativada no cliente e não no servidor).

Verificando as diags do host (novamente: esmague, dirija, leia logs, desfrute de lamentações), vejo que isso é verdade

O tipo de protocolo application / ssl-tls foi enviado para um serviço que não suporta esse tipo de atualização.

"Bem, eu", eu digo, "ativarei a segurança de mensagens no host!" E eu faço. Se você quiser saber como é, é uma cópia exata da configuração do cliente. Olho para cima.

Resultado: Kaboom.

A ligação ('NetTcpBinding', ' http://tempuri.org/ ') suporta streaming que não pode ser configurado junto com a segurança no nível da mensagem. Considere escolher um modo de transferência diferente ou escolher a segurança no nível de transporte.

Portanto, meu host não pode ser transmitido nem protegido por tokens . Catch-22.

tl; dr: Como posso proteger um ponto de extremidade net.tcp WCF transmitido usando WIF ???


3
Ok, provavelmente pergunta ignorante aqui, mas o WIF realmente requer o modo de mensagem? Sons modo de transporte como ele iria trabalhar melhor com a transmissão, algo como o obviamente não testado<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
Joachim Isaksson

3
TransportWithMessageCredentialO modo pode ser outra opção.
Joachim Isaksson

3
O TMLK, MessageSecurity pode assinar e criptografar carga útil em buffer, mas se atrapalha ao lidar com fluxos. Você já pensou em usar authenticationMode = IssuedTokenOverTransport?
OnoSendai

7
Deixe-me ver se consigo convocar alguns fantasmas do passado para ajudar a salvar suas férias. Algumas dicas aqui: social.msdn.microsoft.com/Forums/vstudio/en-US/…
OnoSendai

2
Alguma chance de você postar um projeto de caso de teste que outras pessoas possam experimentar?
Antiduh

Respostas:


41

O WCF tem dificuldades em algumas áreas com streaming (estou olhando para você, MTOM 1 ) devido a um problema fundamental em como ele falha na execução da pré-autenticação da maneira que a maioria das pessoas pensaria que deveria funcionar (isso afeta apenas solicitações subsequentes para esse canal) , não a primeira solicitação) Ok, esse problema não é exatamente o seu, mas siga em frente, pois chegarei ao seu no final. Normalmente, o desafio HTTP funciona assim:

  1. cliente atinge o servidor anonimamente
  2. servidor diz, desculpe, 401, preciso de autenticação
  3. cliente atinge o servidor com token de autenticação
  4. servidor aceita.

Agora, se você tentar ativar o streaming MTOM em um terminal WCF no servidor, ele não irá reclamar. Mas, quando você o configura no proxy do cliente (como deve, eles devem corresponder às ligações), ele explodirá em uma morte ardente. A razão para isso é que a sequência de eventos acima que o WCF está tentando impedir é a seguinte:

  1. cliente transmite arquivo de 100 MB para o servidor anonimamente em um único POST
  2. servidor pede desculpas, 401, preciso de autenticação
  3. cliente transmite novamente arquivo de 100 MB para o servidor com um cabeçalho de autenticação
  4. servidor aceita.

Observe que você acabou de enviar 200 MB ao servidor quando precisou enviar apenas 100 MB. Bem, este é o problema. A resposta é enviar a autenticação na primeira tentativa, mas isso não é possível no WCF sem escrever um comportamento personalizado. Enfim, eu discordo.

Seu problema

Primeiro, deixe-me dizer que o que você está tentando é impossível 2 . Agora, para que você pare de girar suas rodas, deixe-me dizer por que:

Parece-me que agora você está vagando em uma classe de problemas semelhante. Se você ativar a segurança no nível da mensagem, o cliente deverá carregar todo o fluxo de dados na memória antes de poder realmente fechar a mensagem com a função hash usual e a assinatura xml exigidas pelo ws-security. Se precisar ler o fluxo inteiro para assinar a mensagem única (que não é realmente uma mensagem, mas é um único fluxo contínuo), você poderá ver o problema aqui. O WCF precisará transmiti-lo uma vez "localmente" para calcular a segurança da mensagem e transmiti-lo novamente para enviá-lo ao servidor. Isso é claramente uma coisa boba, então o WCF não permite segurança no nível de mensagem para o fluxo de dados.

Portanto, a resposta simples aqui é que você deve enviar o token como um parâmetro para o serviço da Web inicial ou como um cabeçalho SOAP e usar um comportamento personalizado para validá-lo. Você não pode usar o WS-Security para fazer isso. Francamente, isso não é apenas um problema do WCF - não consigo ver como ele poderia funcionar praticamente para outras pilhas.

Resolvendo o problema do MTOM

Este é apenas um exemplo de como eu resolvi meu problema de streaming MTOM para autenticação básica. Talvez você possa entender isso e implementar algo semelhante ao seu problema. O ponto crucial é que, para habilitar seu inspetor de mensagens personalizado, é necessário desativar toda noção de segurança no proxy do cliente (ele permanece ativado no servidor), além do nível de transporte (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

Observe que eu desativei a segurança de transporte aqui porque fornecerei isso usando um inspetor de mensagens e um comportamento personalizado:

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;
    }
}

Portanto, este exemplo é para quem está sofrendo com o problema do MTOM, mas também como um esqueleto para implementar algo semelhante para autenticar seu token gerado pelo serviço de token protegido por WIF principal.

Espero que isto ajude.

(1) Dados grandes e streaming

(2) Segurança da mensagem no WCF (consulte "desvantagens").


MTOM and Basic Authorizatione MTOM e OAuth2 ?
27518 Kiquenet
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.