181 votes

Une situation sans issue empêche la sécurisation d'un service WCF TCP en continu par WIF ; elle ruine mon Noël et ma santé mentale.

J'ai besoin de sécuriser un point d'extrémité de service WCF net.tcp en flux continu à l'aide de WIF . Il doit authentifier les appels entrants par rapport à notre serveur de jetons. Le service est en flux continu car il est conçu pour transférer de grandes quantités de données.

Cela semble impossible. Et si je n'arrive pas à contourner la prise, mon Noël sera gâché et je vais boire jusqu'à la mort dans un caniveau pendant que les joyeux clients enjambent mon corps qui se refroidit lentement. Très sérieux, les gars.

Pourquoi est-ce impossible ? Voici l'impasse.

Sur le client, je dois créer un canal avec l'option GénériqueXmlSecurityToken que je reçois de notre serveur de jetons. Pas de problème.

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

Ai-je dit "no problemo" ? Problemo. En fait, NullReferenceException style problemo.

"Frère," j'ai demandé au Cadre, "est-ce que tu as même un chèque nul ?" Le Framework était silencieux, alors j'ai démonté et trouvé que

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

était la source de l'exception, et que la GetProperty l'appel retournait null . Alors, WTF ? Il s'avère que si j'active la sécurité des messages et que je règle le type de justificatif client sur IssuedToken alors cette propriété existe maintenant dans le ClientFactory (protip : Il n'y a pas d'équivalent "SetProperty" dans IChannel, le bâtard).

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

Doux. Plus de NREs. Cependant, mon client est maintenant fautes à la naissance (je l'aime toujours, tho). En fouillant dans les diagnostics WCF (protip : faites faire ça à vos pires ennemis après les avoir écrasés et chassés devant vous mais juste avant de profiter des lamentations de leurs femmes et enfants), je vois que c'est à cause d'un problème de sécurité entre le serveur et le client.

La mise à niveau demandée n'est pas prise en charge par 'net.tcp://localhost:49627/MyService'. Cela peut être dû à des liaisons incompatibles (par exemple, la sécurité est activée sur le client mais pas sur le serveur).

En vérifiant les diags de l'hôte (encore une fois : écraser, conduire, lire les logs, se lamenter), je vois que ceci est vrai

Le protocole de type application/ssl-tls a été envoyé à un service qui ne prend pas en charge ce type de mise à niveau.

"Eh bien, self", je dis, "Je vais juste activer la sécurité des messages sur l'hôte !" Et je le fais. Si vous voulez savoir à quoi ça ressemble, c'est une copie exacte de la configuration du client. Regarde.

Résultat : Kaboom.

Le binding ('NetTcpBinding',' http://tempuri.org/ ') prend en charge le streaming qui ne peut être configuré avec la sécurité au niveau des messages. Envisagez de choisir un autre mode de transfert ou de choisir la sécurité au niveau du transport.

Donc, mon hôte ne peut pas être à la fois diffusé en continu et sécurisé par des jetons. . Catch-22.

tl;dr : Comment puis-je sécuriser un point de terminaison WCF net.tcp streamé en utilisant WIF ???

41voto

x0n Points 26002

WCF a des problèmes dans quelques domaines avec le streaming (je vous regarde, MTOM). 1 ) en raison d'un problème fondamental dans la façon dont il ne parvient pas à effectuer la pré-authentification de la manière dont la plupart des gens pensent que cela devrait fonctionner (cela n'affecte que les demandes ultérieures pour ce canal, pas la première demande) Ok, donc ce n'est pas exactement votre problème, mais s'il vous plaît suivez le cours car je vais arriver à la vôtre à la fin. Normalement, le défi HTTP fonctionne comme suit :

  1. le client consulte le serveur de manière anonyme
  2. le serveur dit, désolé, 401, j'ai besoin d'une authentification.
  3. le client frappe le serveur avec un jeton d'authentification
  4. Le serveur accepte.

Maintenant, si vous essayez d'activer le streaming MTOM sur un endpoint WCF sur le serveur, il ne se plaindra pas. Mais, lorsque vous le configurez sur le proxy client (comme vous le devriez, ils doivent correspondre aux liaisons), il explosera dans une mort ardente. La raison pour ceci est que la séquence d'événements ci-dessus que WCF essaie d'empêcher est ceci :

  1. Le client transmet un fichier de 100 Mo au serveur de manière anonyme en un seul POST.
  2. le serveur dit "Désolé, 401, j'ai besoin d'une authentification".
  3. Le client transmet à nouveau un fichier de 100 Mo au serveur avec un en-tête d'authentification.
  4. Le serveur accepte.

Remarquez que vous venez d'envoyer 200MB au serveur alors que vous n'aviez besoin que d'envoyer 100MB. Voilà le problème. La réponse est d'envoyer l'authentification à la première tentative mais ce n'est pas possible dans WCF sans écrire un comportement personnalisé. Bref, je m'égare.

Votre problème

Tout d'abord, laissez-moi vous dire que ce que vous essayez est impossible. 2 . Maintenant, pour que vous cessiez de tourner en rond, laissez-moi vous dire pourquoi :

Il me semble que vous errez maintenant dans une catégorie de problèmes similaires. Si vous activez la sécurité au niveau du message, le client doit charger tout le flux de données en mémoire avant de pouvoir fermer le message avec la fonction de hachage habituelle et la signature xml requise par ws-security. S'il doit lire le flux entier pour signer le message simple (qui n'est pas vraiment un message, mais c'est un flux continu simple) alors vous pouvez voir le problème ici. WCF devra lire le flux une fois "localement" pour calculer la sécurité du message, puis le lire à nouveau pour l'envoyer au serveur. C'est clairement une chose stupide, donc WCF ne permet pas la sécurité de niveau de message pour les données de flux.

La réponse simple est donc que vous devez envoyer le jeton soit comme paramètre au service Web initial, soit comme en-tête SOAP et utiliser un comportement personnalisé pour le valider. Vous ne pouvez pas utiliser WS-Security pour ce faire. Franchement, ce n'est pas seulement un problème WCF - je ne vois pas comment cela pourrait fonctionner dans la pratique pour d'autres piles.

Résoudre le problème du MTOM

Il s'agit juste d'un exemple de la façon dont j'ai résolu mon problème de streaming MTOM pour l'authentification de base, donc peut-être que vous pourriez prendre les tripes de ceci et mettre en œuvre quelque chose de similaire pour votre problème. L'essentiel est que pour activer votre inspecteur de messages personnalisé, vous devez désactiver toute notion de sécurité sur le proxy client (elle reste activée sur le serveur), à l'exception du niveau de transport (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

Notez que j'ai désactivé la sécurité du transport ici parce que je la fournirai moi-même en utilisant un inspecteur de messages et un comportement personnalisé :

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

Cet exemple est donc destiné à tous ceux qui sont confrontés au problème du MTOM, mais il peut également vous servir de base pour mettre en œuvre quelque chose de similaire afin d'authentifier votre jeton généré par le service de jeton primaire sécurisé par le WIF.

J'espère que cela vous aidera.

(1) Grandes données et streaming

(2) Sécurité des messages dans WCF (voir "inconvénients").

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X