LA SAGA 7: authentification par formulaire, quelques scénarios
Par amethyste le Sep 8, 2007 | Dans focus | 1 réaction »
Je vais commencer par un rectificatif sur un extrait de code du blog précédent qui contient des erreurs.
Première erreur dans le fichier de configuration:
<authentication mode="Forms"> <forms loginUrl="~/login/login.aspx"> <credentials> <user name="amethyste" password="amethyste"/> </credentials> </forms> </authentication>
J'ai beau être absolument certain d'avoir testé, la réalité m'oblige à dire que cela ne peut pas fonctionner car par défaut ASP attend un mot de passe haché avec l'algorithme SHA1 [3]. Il faut donc tester le code avec le paramétrage suivant:
<authentication mode="Forms"> <forms loginUrl="~/login/login.aspx"> <credentials passwordFormat="Clear"> <user name="amethyste" password="amethyste"/> </credentials> </forms> </authentication>
Evidemment, dans la vie réelle on n'utilisera jamais un tel paramétrage!
Ce n'est pas tout. Le code qui vient ensuite doit plutôt ressembler à ceci:
FormsAuthentication.RedirectFromLoginPage(this.Identifiant.Text, true);
Au lieu de:
FormsAuthentication.RedirectFromLoginPage("nnn", true);
Afin de créer un cookie d'authentification basé sur le login de la personne connectée.
Nous allons rester dans le petit monde de l'authentification par formulaire. Sous ses aspects bonhommes, ce modèle d'authentification présente lui aussi quelques scénarios particuliers:
• Authentification multi-site
• Authentification entre ASP .NET 1.X et 2.0
• Que faire si le navigateur refuse les cookies?
L'authentification multi-site concerne deux situations:
1. Les fermes de site
2. Création d'un SSO pour n'avoir pas à se ré authentifier chaque fois que l'on change de site
Pour tester les exemples qui suivent il suffit de créer deux sites Web, appelons les WebSite1 et WebSite2.
Ces deux sites seront identiques.

La page de login contient le code vu la semaine dernière et corrigé plus haut.
La page principale contient juste un message qui permet d'identifier le site (mais on peut se contenter de l'url) ainsi qu'un bouton de déconnection qui appelle:
FormsAuthentication.SignOut();
Pour finir, le fichier web.config de ces sites contient la même déclaration suivante:
<authentication mode="Forms"> <forms loginUrl="~/login/login.aspx" timeout="1"> <credentials passwordFormat="Clear"> <user name="amethyste" password="amethyste"/> </credentials> </forms> </authentication> <authorization> <deny users="?"/> </authorization>
On va choisir un des sites comme site principal, par exemple WebSite2 et compléter sa page par défaut avec un lien hypertexte que vous adapterez à votre situation:
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="Ici url de WebSite1"> Vers WebSite1 </asp:HyperLink>
L'idée que l'on va tester est que si on s'authentifie sur WebSite2, puis que l'on se dirige vers WebSite1, celui-ci ne nous demandera pas de s'authentifier puisque les paramètres d'authentification sont identiques.
Si on fait l'expérience on constate la séquence suivante:
• On lance WebSite2
• On s'authentifie sur WebSite2
• La page principale de WebSite2 s'ouvre
• On clique vers le lien pour se diriger vers WebSite1
• On tombe sur la fenêtre d'authentification de WebSite1
Cela ne fonctionne donc pas.
Il y a une raison simple qui réside dans la façon dont le ticket d'authentification est crypté.
ASP a besoin de crypter beaucoup de choses comme les tickets d'authentification ou le viewstate. Pour savoir comment il doit procéder, ASP consulte les informations trouvées dans la section <machineKey>. On lira en bibliographie la documentation de cette section [4], pour notre propos il suffit juste de savoir que les attributs validationKey et decryptionKey qui déterminent la clef utilisées pour chiffrer/déchiffrer ainsi que valider les données a la valeur IsolateApp.
Cela signifie qu'une nouvelle clef sera générée pour chaque site.
On comprend donc mieux pourquoi le ticket d'authentification créé dans WebSite2 n'a pu être lu dans WebSite1.
La solution pour que tout marche est donc de fixer à la main une valeur. Par exemple l'utilitaire trouvé en [7] génère ceci:
<machineKey validationKey='2DF98D97141E453FEF5EE … DD42267041BF5C14AE31' decryptionKey='8B681A890EF529F6CE85485F4476EDAB4105148D1C995527' validation='SHA1'/>
Cette fois on constate que l'on accède sans problème à WebSite1 et que si on clique sur le bouton de déconnection, on se déconnecte bien pour les deux sites.
Il est possible que WebSite2 soit un site ASP 2.0, tandis que WebSite1 soit écrit en ASP 1.X. Dans ce cas vous devrez ajouter un l'attribut decryption ainsi:
<machineKey validationKey= '2DF98D97141E453FEF5EE … DD42267041BF5C14AE31' decryptionKey= "8B681A890EF529F6CE85485F4476EDAB4105148D1C995527" validation="SHA1" decryption="3DES" />
L'algorithme de cryptage d'ASP .NET 2.0 est en effet plus efficace. Cet attribut est nouveau, auparavant il n'y avait que validation [2].
On peut souhaiter mettre en place un dernier scénario: on se connecte sur WebSite2, mais c'est WebSite1 qui fournit la page de connexion.
Si vous testez ce scénario vous constaterez que cela ne fonctionne pas, une exception HttpException est levée par RedirectFromLoginPage.
Par défaut on ne peut pas faire de redirection sur un autre site. Mais là aussi <forms> nous offre une échappatoire sous la forme de l'attribut enableCrossAppRedirects.
OK, on dispose d'un ticket d'authentification, celui-ci est crypté puis enpaqueté dans un cookie. Oui, mais supposons que l'on ne souhaite pas utiliser de cookies justement.
La section <forms> accepte un nouvel attribut appelé cookieless destiné à prendre en charge ce genre de scénario. Les valeurs possibles sont UseCookies, UseUri, UseDeviceProfil et AutoDetect.
UseCookies est la valeur par défaut. UseUri demande à ASP de stocker les informations d'authentification dans l'url. Par exemple celle-ci prendra la forme suivante:
Rigolo non?
Vous pourriez vous inquiéter de voir surgir quelque chose d'aussi indigeste dans vos url, surtout si vous faites du mapping d'url derrière.
Pas de panique, en fait l'url est interprétée par un module spécialisé qui se chargera du nettoyage, Request.Path retournera quelque chose de parfaitement propre. Vous n'avez qu'à paramétrer et ASP se chargera automatiquement de créer vos url, on ne peut pas faire plus simple!
UseDeviceProfil demande à ASP d'examiner les capacités du navigateur afin de déterminer quelle méthode utiliser tandis qu'AutoDetect tente d'abord d'utiliser le cookie, si cela échoue alors il se rabat sur l'url.
Rappelez vous tout de même, cet attribut n'existe qu'avec ASP .NET 2.0, pour les versions antérieures vous devrez faire le travail à la main [5].
Il existe tout de même une difficulté dans le cas où slidingExpiration = true. La documentation raconte ceci [9]:
Lorsque SlidingExpiration a la valeur true, l'intervalle de temps pendant lequel le cookie d'authentification est valide est réinitialisé sur la valeur de propriété Timeout de l'expiration. Cela se produit si l'utilisateur navigue après l'expiration de la moitié du délai d'attente. Par exemple, si vous définissez un délai d'expiration de 20 minutes via l'expiration décalée, un utilisateur peut visiter le site à 14 h 00 et recevoir un cookie configuré pour expirer à 14 h 20. L'expiration n'est mise à jour que si l'utilisateur visite le site après 14 h 10. Si l'utilisateur visite le site à 14 h 09, le cookie n'est pas mis à jour, car la moitié du délai d'expiration n'a pas été dépassée. Si l'utilisateur attend ensuite 12 minutes, et s'il visite le site à 14 h 21, le cookie aura expiré.
Donc une fois franchit le seuil de 50% de la durée de vie du cookie, ASP va rafraîchir le ticket d'authentification ce qui dans notre cas va modifier l'url. Seulement contrairement à un cookie il n'est pas possible de modifier comme cela une url de sorte que le navigateur reprenne comme si de rien n'était, on doit obligatoirement faire une redirection.
C'est la raison pour laquelle dans ce contexte le client verra la page de reconnexion apparaître beaucoup plus vite que ce qui était prévu [10]. Il faut donc soit en tenir compte dans vos réglages, soit ne pas utiliser slidingExpiration=true si cookieless=UseUri.
Pour terminer une remarque:
l'authentification par formulaire n'est pas un modèle d'authentification très fiable si vous n'utilisez pas SSL. Le cookie d'authentification est en effet envoyé pour chaque requête, fusse pour charger une image. Il est donc assez facile pour un attaquant de le détourner.
Une critique fréquente faite est que SSL est lent. A vous de voir si la critique est fondée. Souvent on utilise un compromis: activer SSL uniquement pour la page d'authentification.
Dans ce cas je recommande de compléter <forms> avec l'attribut requireSSL fixé à true. Dans ce mode le cookie d'authentification ne sera pas envoyé pour les pages où SSL n'est pas activé.
Côté code vous recevrez une HttpException lors de l'appel à RedirectFromLoginPage si vous oubliez d'installer SSL. De plus User.Identity.IsAuthenticated retournera toujours false pour les pages qui ne sont pas en SSL.
La façon dont les cookies sont pris en charge est réglée par la section <httpCookies> du fichier de configuration [6]. On y retrouve requireSSL qui fixe la valeur par défaut que l'on retrouve par exemple dans <forms>.
On trouve aussi l'attribut httpOnlyCookies. Cet attribut active une fonctionnalité spécifique à IE 6 et indique au navigateur que le cookie ne doit pas être accessible par un script.
Dans tous les cas, fixez une durée limite au cookie, un cookie d'authentification avec une durée très longue ou pire illimité ce n'est pas sérieux.
Bibliographie
[1] Authentification multi site:
http://www.developer-corner.com/Resources/KnowledgeBase/tabid/118/articleType/ArticleView/articleId/23/Default.aspx
[2] Configuration de machineKey:
http://msdn2.microsoft.com/en-us/library/ms998288.aspx
[3] Doc de credentials:
http://msdn2.microsoft.com/en-us/library/e01fc50a.aspx
[4] Documentation de machineKey:
http://msdn2.microsoft.com/fr-fr/library/w8h3skw9.aspx
[5] cookieless sans ASP .NET 2.0:
http://www.codeproject.com/aspnet/cookieless.asp
[6] Documentation de httpCookies:
http://msdn2.microsoft.com/fr-fr/library/ms228262.aspx
[7] Générateur de machineKey:
http://www.eggheadcafe.com/articles/GenerateMachineKey/GenerateMachineKey.aspx
[8] Comment générer un machineKey:
http://www.eggheadcafe.com/articles/20030514.asp
[9] Documentation de slidingExpiration
http://msdn2.microsoft.com/fr-fr/library/system.web.configuration.formsauthenticationconfiguration.slidingexpiration.aspx
[10] Problème de cookieless=UseUri et slidingExpiration=true
http://blogs.msdn.com/carloc/archive/2006/11/11/don-t-slide-your-expiration-without-cookies.aspx
1 commentaire
Exemple, j'écris le mot arbre, à l'intérieur de ces guillemets: >
Laisser un commentaire
| « LA SAGA 8: un peu de tout | LA SAGA 6: les cookies et les tickets d'authentification » |