Un « double-hop » survient lorsqu’une application serveur (typiquement une application Web ASP.NET) tente de s’authentifier avec les accréditations de l’utilisateur client sur un système « back-end » :

 

Schéma d'un double-hop 

Le problème que l’on rencontre couramment est un souci d’authentification entre les deux serveurs. En effet, les accréditations de l’utilisateur ne peuvent pas être transmises entre le premier serveur et le second. Si le premier serveur tente de s’authentifier avec l’identité de l’utilisateur, le second serveur ne pourra pas reconnaître cette authentification, et il y aura une erreur d’authentification (erreur HTTP 401 par exemple).

 

Pour comprendre la cause de ce problème, rappelons rapidement quelques principes :

  • Un processus possède une identité propre, qui peut-être un compte machine par défaut (comme Local System), un compte utilisateur local, ou un compte utilisateur de l’AD. Le code du processus est exécuté sous cette identité.
  • Un thread possède par défaut la même identité que son processus parent. On peut cependant modifier cette identité dans un thread donné : on parle alors d’impersonation. Plusieurs threads dans un même processus peuvent donc tourner sous différentes identités.
    Il est également possible pour un thread impersonifié de revenir à l’identité par défaut du processus : c’est l’opération de revert-to-self.
  • Dans une application ASP.NET, l’identité du processus est définie par l’identité du pool d’application. Les threads qui vont exécuter les requêtes clientes sont impersonifiés ou pas en fonction de l’attribut impersonate de la balise identity du web.config. L’identité emprunté par le thread peut prendre 2 valeurs :
      •    Si l’application Web est configurée en anonyme, c’est l’identité spécifiée dans IIS pour le compte anonyme qui sera utilisée par le thread.
      • Si une authentification (Basique, Windows Intégrée, etc…) est configurée dans IIS, c’est l’identité de l’utilisateur qui sera utilisée par le thread.

Le problème de double-hop peut se poser dans ce dernier cas : lorsque l’application ASP.NET récupère l’identité de l’utilisateur, tout le code de l’application s’exécute sous cette identité. Une connexion à une base de données ou un service Web se fera donc (à défaut d’accréditations explicites) sous cette identité.

Le serveur back-end refusera alors la connexion puisqu’il est impossible au serveur frontal de prouver l’identité de l’utilisateur qu’il impersonifie (le système d’authentification NTLM ne permet qu’aux possesseurs du mot de passe de s’authentifier ; hors le serveur ASP.NET ne connaît pas ce mot de passe).

 

Il existe plusieurs options pour éviter ce comportement :

 

1.       Utilisation de l’authentification basique + SLL (pour éviter que le mot de passe transite en clair) : le serveur Web aura alors connaissance du mot de passe de l’utilisateur et pourra le réutiliser pour s‘authentifier sur le serveur back-end

2.       Utilisation de l’authentification Kerberos + délégation : un jeton d’authentification pourra alors passer entre les serveurs. C’est la méthode à préférer dans des environnements Intranet + AD.

3.       Faire un revert-to-self (par code) pour forcer le thread à utiliser l’identité du process : toutes les connexions au back-end se feront alors sous cette même identité.

4.       Utiliser un authentification FBA (Form Based Authentication) : on récupère les accréditations de l’utilisateur, on vérifié son identité par code, et on réalise soi-même l’impersonation. Le serveur connaît alors les accréditations de l’utilisateur et peut les utiliser pour s’authentifier au près du back-end.

5.       Autres solutions : COM+, accréditations dans le web.config + encryption,

 

Je vous propose de passer en revue quelques unes de ces options dans de prochains posts…

 

-= Julien Bakmezdjian =-