36 votes

Authentification d'un utilisateur ou d'un mot de passe à l'aide de RESTful WCF et de Windows Forms

Quelle est la meilleure approche pour mettre en œuvre l'autorisation/authentification pour une application Windows Forms qui communique avec un service WCF RESTful hébergé par IIS ?

La raison pour laquelle je pose cette question est que je suis très confus, après avoir parcouru différents articles et posts exprimant une méthode différente et finalement être tombé sur un document de ~650 pages sur les "WCF Security Best Practices" (les meilleures pratiques en matière de sécurité WCF) ( http://www.codeplex.com/WCFSecurityGuide ) Je ne sais pas quelle est la MEILLEURE approche à adopter et comment commencer la mise en œuvre, compte tenu de mon scénario.

J'ai commencé par cet article "A Guide to Designing and Building RESTful Web Services with WCF 3.5" ( http://msdn.microsoft.com/en-us/library/dd203052.aspx ) et une vidéo du PDC sur les services WCF RESTful, qui était excellente et m'a aidé à mettre en œuvre mon premier service WCF REST-friendly,

Après avoir fait fonctionner le service, je suis revenu pour mettre en place la sécurité, voir. "Security Considerations" (au quart de la page) et j'ai essayé d'implémenter un en-tête HTTP Authorization selon les instructions, mais j'ai trouvé le code incomplet (voir comment la variable 'UserKeys' n'a jamais été déclarée). C'est à ce moment que j'ai essayé de faire des recherches plus approfondies sur la façon de procéder (en utilisant un hachage HMAC avec l'en-tête HTTP "Authorization", mais je n'ai pas trouvé grand chose sur google ?) cela m'a conduit à d'autres articles concernant la sécurité au niveau du message, l'authentification des formulaires et les validateurs personnalisés et franchement je ne suis pas sûr de savoir quelle est la meilleure approche et la plus appropriée à adopter maintenant.

Donc, tout ceci étant dit (et merci d'avoir écouté jusqu'à présent !), je pense que mes principales questions sont les suivantes,

**- Quelle implémentation de la sécurité dois-je utiliser ?

- Existe-t-il un moyen d'éviter d'envoyer le nom d'utilisateur/mot de passe à chaque appel WCF ? Je préférerais ne pas envoyer ces octets supplémentaires si une connexion a été établie au départ, ce qui sera le cas avant que les appels ultérieurs ne soient autorisés à être effectués après la connexion.

- Dois-je vraiment me préoccuper d'autre chose que du texte en clair si j'utilise SSL ?**

Comme indiqué, .NET 3.5 win forms app, service WCF hébergé par IIS, mais ce qui est important, c'est que je souhaite que tous les services WCF requièrent cette procédure d'autorisation (quelle qu'elle soit, session, en-tête http ou autre), car je ne veux pas que n'importe qui puisse accéder à ces services à partir du web.

Je sais que le message ci-dessus est volumineux mais je devais exprimer le chemin que j'ai déjà parcouru et ce que je dois accomplir, toute aide est grandement appréciée.

PS : J'ai également pris connaissance de ce post Comment configurer des services RESTful sécurisés avec WCF en utilisant nom d'utilisateur/mot de passe + SSL et si la communauté suggère que je m'éloigne de REST pour les services WCF, je peux le faire, mais j'ai commencé par cela pour garder la cohérence pour toutes les API publiques à venir.

Je pense qu'il est important d'indiquer comment j'accède à mon service WCF (le contact avec le service fonctionne, mais quelle est la meilleure façon de valider les informations d'identification - et de renvoyer ensuite l'objet Membre ?)

WebChannelFactory<IMemberService> cf = new WebChannelFactory<IMemberService>(
                new Uri(Properties.Settings.Default.MemberServiceEndpoint));
            IMemberService channel = cf.CreateChannel();
            Member m = channel.GetMember("user", "pass");

Code à moitié implémenté à partir de l'article de MS (et un peu de mon propre code pour les tests) :

 public Member GetMember(string username, string password)
    {
        if (string.IsNullOrEmpty(username))
            throw new WebProtocolException(HttpStatusCode.BadRequest, "Username must be provided.", null);
        if (string.IsNullOrEmpty(password))
            throw new WebProtocolException(HttpStatusCode.BadRequest, "Password must be provided.", null);

        if (!AuthenticateMember(username))
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
            return null;
        }

        return new Member() { Username = "goneale" };
    }

17voto

Nicholas Piasecki Points 13681

Je n'ai aucune expérience des capacités REST de WCF, mais j'ai eu beaucoup de mal à comprendre les implications des choix de sécurité en matière de ma question sur la sécurité WCF . Comme vous l'avez remarqué, il y a un réel manque de documentation sur WCF sur le Web, et mon expérience REST est limitée, alors prenez mes réponses avec un grain de sel :

Quelle implémentation de la sécurité dois-je utiliser ?

et

Dois-je vraiment me préoccuper d'autre chose que du texte en clair si j'utilise SSL ?

L'authentification de base par SSL est tout à fait possible - après tout, c'est ainsi qu'un grand nombre de sites Web existants authentifient les utilisateurs. (Lorsque vous vous connectez à votre compte d'achat Amazon, ils transmettent simplement votre nom d'utilisateur et votre mot de passe tels que vous les avez tapés via une connexion SSL). Je comprends ce que l'article dit à propos de la sécurité et des attaques par dictionnaire, mais bla bla bla, il faut rester simple et commencer par faire fonctionner quelque chose. L'API Plain Old XML d'UPS demande le nom d'utilisateur et le mot de passe à chaque appel, tout comme l'API POX de FedEx, l'API SOAP de PayPal et l'API SOAP de CyberSource - cela semble être suffisant pour une utilisation dans le monde réel.

Existe-t-il un moyen d'éviter d'envoyer le nom d'utilisateur/mot de passe à chaque appel WCF ? Je préférerais ne pas envoyer ces octets supplémentaires si une connexion a été établie au départ, ce qui sera le cas avant que les appels ultérieurs ne soient autorisés à être effectués après la connexion.

C'est une question à laquelle je peux répondre avec un peu plus d'assurance. En général, nous essayons de concevoir nos services WCF publics de manière à ce qu'ils soient sans état. De cette façon, nos services WCF s'adaptent facilement ; il suffit d'ajouter du matériel, des serveurs et des équilibreurs de charge au problème, et nous n'avons pas à nous soucier des sessions collantes ou du maintien de l'état de la session quelque part. Cela signifie que si nous voulons "garder un utilisateur connecté", ce n'est pas quelque chose qui va se produire sur le serveur.

Ce que j'ai fini par faire, c'est traiter mon site web comme un sous-système de confiance. Il s'authentifie auprès du service WCF à l'aide d'un certificat X509 pré-partagé, et si un client est connecté au site Web via l'authentification par formulaire, il envoie un en-tête de nom d'utilisateur du client au service ; un comportement de point final personnalisé sur le service WCF recherche cet en-tête, constate qu'il a été installé par un sous-système de confiance et procède à l'usurpation de l'identité de l'utilisateur sans que le mot de passe de l'utilisateur n'ait besoin d'être fourni ou vérifié auprès de la base de données.

Puisque vous utilisez REST, vous pouvez probablement utiliser un cookie côté client pour maintenir l'état. Si vous utilisez le mode de compatibilité ASP.NET, je pense que vous pouvez même utiliser directement l'authentification des formulaires, mais je ne connais pas bien cette approche car mon service WCF n'était pas hébergé par IIS.

En bref, vous devrez envoyer quelque chose à chaque demande pour identifier l'utilisateur, qu'il s'agisse du nom d'utilisateur et du mot de passe, du seul nom d'utilisateur ou d'une valeur hachée stockée dans le cookie. Dans le cas de la dernière option, je suppose qu'il faut avoir une sorte de Login() ou quelque chose du genre sur le service, quelque chose qui enverrait un "ok, vous êtes enregistré si vous transmettez cette valeur de hachage lors de vos prochaines requêtes". Mais tous les clients REST ne s'attendent pas à recevoir des cookies, juste de simples requêtes GET/PUT/POST/DELETE sans aucun état.

À ma place, j'opterais pour l'approche du sous-système de confiance (où un en-tête de nom d'utilisateur est fourni avec des informations d'identification pré-partagées pour le sous-système) ou j'exigerais une authentification à chaque appel. Le service disposerait probablement d'un mécanisme de mise en cache de l'authentification très performant si toutes ces demandes répétées devenaient un problème.

J'espère que cela vous aidera un peu.

7voto

Tawani Points 3914

Utilisation de l'authentification de base :

WebHttpBinding binding = new WebHttpBinding();
binding.SendTimeout = TimeSpan.FromSeconds(25);
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

Uri address = new Uri("http://localhost:3525/WcfRestWeb/Quotes.svc");

WebChannelFactory<IQuoteService> factory =
             new WebChannelFactory<IQuoteService>(binding, address);

factory.Credentials.UserName.UserName = "tan";
factory.Credentials.UserName.Password = "wani";

IQuoteService proxy = factory.CreateChannel();

var response = proxy.GenerateQuote(GetQuoteRequest());
Console.WriteLine("Quote Amount: " + response.QuoteAmount);

3voto

GONeale Points 9432

Merci pour les réponses. En prenant du recul et en regardant clairement et objectivement le problème dans son ensemble (en d'autres termes, en ignorant les 4 heures et plus que j'ai investies dans les services RESTful), j'essaie de faire fonctionner la chose sans REST pour l'instant et les références que j'essaie de suivre en ce moment sont les suivantes : -

Cela semble correspondre à ce que je souhaite.

lextm : Je vous comprends, après avoir écrit ce billet, j'ai parcouru de plus près le guide de sécurité WCF et j'ai pris des notes sur toutes mes exigences basées sur les options qu'ils veulent que vous réfléchissiez à chaque principe.

J'ai choisi :
- Mode de sécurité du transfert : Sécurité du transport
- Auth. Option : Sécurité de base
- Binding : wsHttpBinding
- Authentification personnalisée avec validateur de nom d'utilisateur

À la lumière des exemples fournis pour chacun d'entre eux et en examinant le cas d'utilisation des formulaires Windows avec le service WCF, il semble que ce soit la meilleure façon de procéder.

Nicholas : Je suis d'accord, concevoir les services de manière à ce qu'ils soient sans état est probablement une meilleure approche.

D'après l'article que je suivrai quand j'aurai le temps, il utilise le certificat X509 que je ne connais pas du tout (je comprends que vous utilisez ce Nicholas). Est-ce que cela va aller étant donné que cette application client peut être téléchargée sur Internet et installée sur le PC de n'importe quelle personne qui a un compte sur mon site Web ?

Merci pour votre aide, Graham

PS : Je pense que c'est le cas d'utilisation le plus proche de mon scénario (sauf que je souhaite utiliser la sécurité des transports), devrais-je envisager de mettre en œuvre cette solution puisqu'elle ne nécessite pas de certificat ? D'après la citation que j'ai lue, je pourrais avoir besoin du certificat car "Le cryptage du certificat X509 est requis par WCF parce que les informations d'identification du client (nom d'utilisateur/mot de passe) sont transmises en texte clair dans le message SOAP". - Cependant, d'après ce que j'ai appris et ce que nous avons dit, si j'utilise SSL, ce point est probablement discutable ?

1voto

Lex Li Points 18214

En fait, vous ne devriez pas accorder autant d'attention à la partie WinForms, car c'est la partie WCF qui est la plus importante.

Au fait, avez-vous lu attentivement ces pages ?

Concept http://www.codeplex.com/WCFSecurityGuide/Wiki/View.aspx?title=Ch%2005%20-%20Authentication,%20Authorization%20and%20Identities%20in%20WCF&referringTitle=Home

et comment http://www.codeplex.com/WCFSecurity/Wiki/View.aspx?title=How%20Tos&referringTitle=Home

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