web-dev-qa-db-fra.com

Authentification client-serveur - en utilisant SSPI?

Je travaille sur une application client-serveur et je souhaite que le client s'authentifie auprès du serveur à l'aide des informations d'identification de connexion de l'utilisateur, mais je ne souhaite pas que l'utilisateur doive taper son nom d'utilisateur et son mot de passe. Je ne veux certainement pas être responsable de la gestion sécurisée des mots de passe. Je veux seulement que l'utilisateur me prouve qu'il est bien ce qu'il prétend être, et ensuite mon serveur peut continuer et accorder/refuser les commandes à sa guise.

Mes utilisateurs font partie d'un domaine et je souhaite donc pouvoir utiliser les informations d'identification d'ouverture de session qu'ils ont créées lors de leur connexion.

Je n'utilise aucun type de service Web et je ne le veux pas non plus. Je contrôle à la fois le logiciel client et le logiciel serveur, et les deux sont écrits en C # pur et utilisent de bonnes sockets pour faire le travail.

Je préférerais faire cela avec C # /. Net pur, mais je suis ouvert à l’utilisation de C # non sécuritaires et de pinvokes à l’API Win32 si cela signifie que je vais faire le travail.

J'ai lu un peu sur l'ISPS dans Windows, mais je me sens un peu dans le noir depuis que ce type de développement d'applications est nouveau pour moi.

Est-ce que quelqu'un sait comment faire cela? Le SSPI est-il le bon chemin? Comment utiliser SSPI depuis C #? Existe-t-il une méthode .Net-native pour que mon code puisse rester portable?

25
antiduh

Mettre à jour:

SSPI est la bonne approche pour cela. L'API n'est pas trop difficile à utiliser, mais nécessite un projet de taille décente pour être intégré à C #. 

Dans le processus de recherche des éléments nécessaires pour résoudre cette question, j'ai écrit un projet visant à fournir un SSPI en .Net. Ci-dessous, je décris les bases de l'interfaçage avec l'API Windows SSPI afin que quiconque puisse répliquer mes résultats. Si vous souhaitez utiliser SSPI dans .Net, je peux vous suggérer d'utiliser le projet que j'ai créé pour résoudre ce problème:

NSSPI - UNE INTERFACE .NET POUR L'API SSPI

SSPI vous fournit des tableaux d'octets bruts contenant des jetons d'authentification que vous décidez ensuite de transmettre - que ce soit sur un socket avec des messages au format binaire, un canal XML personnalisé, .Net Remoting, une forme de WCF, enfin, même un port série. Vous devez décider comment traiter avec eux. Avec SSPI, un serveur peut authentifier les clients, l'identifier de manière sécurisée et même effectuer des procédures de base de traitement des messages, telles que le cryptage/la signature, à l'aide du contexte de sécurité établi avec le client.

L'API SSPI est documentée ici: vue d'ensemble de l'API SSPI

Jetez un coup d'œil aux fonctions suivantes:

  • (AcquireCredentialsHandle }
    • Acquiert un descripteur d'informations d'identification (par exemple, la connexion de l'utilisateur actuel). Utilisé par les serveurs et les clients.
  • InitializeSecurityContext
    • Utilisé par les clients pour établir un contexte de sécurité avec un serveur.
  • AcceptSecurityContext
    • Utilisé par les serveurs pour établir un contexte de sécurité avec un client.

Le flux de travail typique est que chaque partie initialise ses informations d'identification à l'aide de AcquireCredentialsHandle. Le cycle d'authentification commence alors et progresse comme suit:

  • Le client appelle InitializeSecurityContext sans fournir de jetons d'entrée, ce qui retourne les jetons de sortie sous la forme d'un tableau d'octets. ISC renvoie 'ContinueNeeded' pour indiquer que le cycle d'authentification n'est pas terminé.
  • Le client envoie les jetons au serveur par n'importe quel moyen souhaité.
  • Le serveur alimente les jetons reçus en entrée dans AcceptSecurityContext et produit ses propres jetons de sortie. ASC renvoie également 'ContinueNeeded' pour indiquer que le cycle d'authentification N'est pas terminé.
  • Le serveur envoie ensuite ses jetons de sortie au client.
  • Le client fournit les jetons de serveurs en tant qu'entrée à InitializeSecurityContext, qui renvoie les nouveaux jetons de sortie. 
  • Le client envoie ses nouveaux jetons de sortie au serveur.
  • ...

Ce cycle se poursuit jusqu'à ce que le client voie InitializeSecurityContext «OK» et que le serveur voit AcceptSecurityContext «OK». Chaque fonction peut renvoyer 'OK' et toujours fournir un jeton de sortie (comme indiqué par un retour non nul), pour indiquer qu'elle doit toujours envoyer des données à l'autre côté. C'est ainsi que le client sait que sa moitié est terminée mais que celle du serveur est encore incomplète. et vice versa si le serveur termine avant le client. Le côté achevé en premier (retourne «OK») dépend du paquet de sécurité spécifique utilisé sous le capot par SSPI, et tout consommateur SSPI devrait en être conscient.

Les informations ci-dessus devraient être suffisantes pour que quiconque s'interface avec le système SSPI afin de fournir une «authentification intégrée Windows» dans leur application et de répliquer mes résultats.

Vous trouverez ci-dessous ma réponse précédente, car j'ai appris à appeler l'API SSPI.


J'avais oublié cette question et je suis revenu par hasard à ce problème quelques jours auparavant, sur un coup de tête. J'ai besoin de résoudre ce problème dans un an ou deux cependant :)

C'est possible en .Net, et je développe actuellement un wrapper SNet .Net que je compte publier. 

J'ai basé mon travail sur quelques exemples SSPI de Microsoft que j'ai trouvés.

L'exemple contient un assembly géré par C++/CLI qui implémente les composants nécessaires de l'API SSPI (dans le dossier Microsoft\Samples\Security\SSPI\SSPI extrait du fichier REMSSPI.exe). Ils ont ensuite deux interfaces utilisateur, une application cliente et une application serveur, toutes deux écrites en C #, qui utilisent cette API pour effectuer l'authentification SSPI.

Les interfaces utilisateur utilisent la fonctionnalité de communication à distance .Net pour tout relier, mais si je comprends bien l'API SSPI, les seules informations que le client et le serveur doivent échanger consistent en octets [] contenant des données de jeton de contexte de sécurité, qui peuvent être facilement intégré à l’infrastructure de communication de votre choix; dans mon cas, un protocole binaire de ma propre conception.

Quelques remarques sur la manière de faire fonctionner l'échantillon - ils ont la source de bibliothèque 'SSPI', qui compile le mieux sous VS 2005, bien que je l'ait obtenu sous 2008; 2010 ou une version ultérieure nécessiterait des modifications, car ils utilisent des constructions de langage obsolètes. Vous devrez peut-être également modifier les fichiers d’en-tête faisant partie du kit de développement logiciel (SDK) de votre plate-forme, car ils utilisent les affectations de pointeur const pour les variables non résolues et je ne connais pas de meilleur moyen de rendre le compilateur heureux (je n’ai jamais utilisé C++/CLI avant).

Ils incluent une dll SSPI compilée dans le dossier Microsoft\Samples\Security\SSPI\bin. Pour que les fichiers binaires client/serveur fonctionnent, vous devez copier cette dll dans leur répertoire bin, sinon la résolution de l’assemblage échoue.

Donc, pour résumer:

  • Allez ici pour télécharger l'exemple REMSSPI.exe au format Zip à extraction automatique. 
  • Extraire le fichier REMSSPI.exe (deux fois ..)
  • Microsoft\Samples\Security\SSPI\
    • bin\ - contient la DLL compilée Microsoft.Samples.Security.SSPI.dll
    • SSPI\ - contient la source dans la DLL
    • Sample\ - contient le code source de l'interface utilisateur
      • bin\ - contient des exemples d'interface utilisateur de génération. Copiez le fichier SSPI.dll ici et exécutez ControlPanel.Client.exe et ControlPanel.Server.exe
20
antiduh

Nikola a raison. il n'existe pas de méthode .NET native pour accomplir ce que vous faites (du moins, en n'utilisant pas le support de bas niveau .NET Sockets). Vous pouvez certainement plonger sous les couvertures pour faire de la magie noire interop, mais si le client et le serveur sont sous votre contrôle, vous voudrez peut-être envisager de remonter un peu la pile et d'utiliser une API de niveau supérieur, telle que WCF, qui prendre en charge .NET-native pour l'authentification Windows intégrée.

En fonction de votre question et de l’environnement décrit, vous pourrez utiliser NetTcpBinding, qui offre des performances élevées, ainsi que la plomberie du flux d’authentification/identité que vous recherchez (il offre également un moyen relativement propre de gérer les autorisations. en utilisant la classe ServiceAuthorizationManager). Sans connaître les spécificités de votre application/service, je ne peux pas vous fournir un "Comment faire" pour mettre en œuvre ce que vous cherchez à faire, mais je peux vous diriger vers la documentation qui propose un exemple assez simple.

0
Andy Hopper

comment le serveur confirme-t-il un client "ils sont qui ils prétendent être"?

Réponse: les fonctions - "InitializeSecurityContext" et "AcceptSecurityContext" ne feraient pas le travail de preuve, elles aident uniquement le serveur à obtenir les informations de connexion du client, qui existaient dans Security Contex. Le contexte de sécurité est le 6ème paramètre de AcceptSecurityContext, son type est "CtxtHandle"

si le serveur veut confirmer si le client est bien ce qu'il dit, il lui suffit d'obtenir le nom d'utilisateur du domaine de l'utilisateur à partir du contexte de sécurité:

  QueryContextAttributes(&m_securitycontext, SECPKG_ATTR_NATIVE_NAMES, &pinfo);

la valeur: pinfo.sClientName est le vrai nom d'utilisateur du client

si le client dit au serveur qu'il est "Bob", si pinfo.sClientName est "Bob", le serveur confirmera que le client dit la vérité.

0
winnie_quest

Je me suis totalement trompé avec WindowsIdentity (ce qui est bon pour autorisation ) car j'ai oublié que WCF gère beaucoup de choses avec les fichiers de configuration, la sécurité des terminaux et la sécurité des messages/transport. 

Avez-vous essayé avec NegotiateStream ? L'exemple donné semble mieux répondre à vos besoins: il utilise Kerberos pour l'authentification avant d'autoriser toute lecture/écriture. L'utilisation du CredentialCache.DefaultNetworkCredentials devrait vous éviter de demander un mot de passe. 

0
xum59