J'ai une exigence pour sécuriser un point de terminaison de service WCF net.tcp en streaming à l'aide de WIF . Il doit authentifier les appels entrants sur notre serveur de jetons. Le service est diffusé car il est conçu pour transférer de grandes quantités de données.
Cela semble impossible. Et si je ne peux pas contourner le piège, mon Noël sera ruiné et je me boirai à mort dans une gouttière pendant que de joyeux acheteurs enjambent mon corps qui se refroidit lentement. Totes sérieux, vous les gars.
Pourquoi est-ce impossible? Voici le Catch-22.
Sur le client, je dois créer un canal avec le GenericXmlSecurityToken que j'obtiens 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 "pas de problème"? Problemo. En fait, le NullReferenceException
style problemo.
"Bro," ai-je demandé au Framework, "est-ce que vous êtes même nul?" Le cadre était silencieux, j'ai donc démonté et trouvé que
((IChannel)(object)tChannel).
GetProperty<ChannelParameterCollection>().
Add(federatedClientCredentialsParameter);
était la source de l'exception, et que l' GetProperty
appel revenait null
. Alors, WTF? Il s'avère que si j'active la sécurité des messages et que je règle le type d'informations d'identification du client sur, IssuedToken
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>
Sucré. Plus de NRE. Cependant, maintenant mon client est blâmé à la naissance (l'aime toujours, tho). En creusant dans les diagnostics WCF (protip: faites faire cela à vos pires ennemis après les avoir écrasés et les avoir conduits avant vous mais juste avant de profiter des lamentations de leurs femmes et enfants), je vois que c'est à cause d'un décalage 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 et non sur le serveur).
En vérifiant les diags de l'hôte (encore une fois: écraser, conduire, lire les journaux, profiter des lamentations), je vois que c'est vrai
Le type de protocole application / ssl-tls a été envoyé à un service qui ne prend pas en charge ce type de mise à niveau.
"Eh bien, moi-même," dis-je, "je vais simplement activer la sécurité des messages sur l'hôte!" Et je fais. Si vous voulez savoir à quoi il ressemble, c'est une copie exacte de la configuration du client. Chercher.
Résultat: Kaboom.
La liaison ('NetTcpBinding', ' http://tempuri.org/ ') prend en charge le streaming qui ne peut pas être configuré avec la sécurité au niveau des messages. Pensez à choisir un mode de transfert différent ou à choisir le niveau de sécurité du transport.
Ainsi, mon hôte ne peut pas être à la fois diffusé et sécurisé via des jetons . Catch-22.
tl; dr: Comment puis-je sécuriser un point de terminaison WCF net.tcp en streaming en utilisant WIF ???
<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
TransportWithMessageCredential
le mode peut être une autre option.Réponses:
WCF a des pièges 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 comme la plupart des gens penseraient 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 veuillez suivre car j'arriverai au vôtre à la fin. Normalement, le défi HTTP fonctionne comme ceci:
Maintenant, si vous essayez d'activer le streaming MTOM sur un point de terminaison WCF sur le serveur, cela ne se plaindra pas. Mais, lorsque vous le configurez sur le proxy client (comme vous devriez, ils doivent correspondre aux liaisons), il explosera dans une mort ardente. La raison en est que la séquence d'événements ci-dessus que WCF tente d'empêcher est la suivante:
Notez que vous venez d'envoyer 200 Mo au serveur alors que vous ne deviez envoyer que 100 Mo. Eh bien, c'est le problème. La réponse est d'envoyer l'authentification à la première tentative, mais cela n'est pas possible dans WCF sans écrire un comportement personnalisé. Quoi qu'il en soit, je m'éloigne du sujet.
Ton problème
Tout d'abord, laissez-moi vous dire que ce que vous essayez est impossible 2 . Maintenant, pour que vous arrêtiez de faire tourner vos roues, laissez-moi vous dire pourquoi:
Il me semble que vous vous promenez maintenant dans une catégorie similaire de problèmes. Si vous activez la sécurité au niveau du message, le client doit charger l'intégralité du flux de données en mémoire avant de pouvoir fermer le message avec la fonction de hachage habituelle et la signature xml requises par ws-security. S'il doit lire l'intégralité du flux pour signer le message unique (ce qui n'est pas vraiment un message, mais c'est un flux continu unique), vous pouvez voir le problème ici. WCF devra le diffuser une fois "localement" pour calculer la sécurité du message, puis le diffuser à nouveau pour l'envoyer au serveur. C'est clairement une chose stupide, donc WCF n'autorise pas la sécurité au niveau des messages pour les données en continu.
Donc, la réponse simple ici est que vous devez envoyer le jeton soit en tant que paramètre au service Web initial, soit en tant qu'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 pratiquement fonctionner pour d'autres piles.
Résolution du problème MTOM
Ceci est juste pour un exemple comment j'ai résolu mon problème de streaming MTOM pour l'authentification de base, alors peut-être que vous pourriez prendre le courage de cela et mettre en œuvre quelque chose de similaire pour votre problème. L'essentiel est que pour activer votre inspecteur de message personnalisé, vous devez désactiver toute notion de sécurité sur le proxy client (elle reste activée sur le serveur,) en dehors 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 car je le fournirai moi-même à l'aide d'un inspecteur de messages et d'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; } }
Ainsi, cet exemple est destiné à toute personne souffrant du problème MTOM, mais également en tant que squelette pour que vous implémentiez quelque chose de similaire pour authentifier votre jeton généré par le service de jeton sécurisé principal par WIF.
J'espère que cela t'aides.
(1) Données volumineuses et streaming
(2) Sécurité des messages dans WCF (voir «inconvénients»).
la source
MTOM and Basic Authorization
, et MTOM et OAuth2 ?