Authentification par formulaire « minimaliste »

L'authentification par formulaire – également appelée authentification par cookie – est aujourd'hui énormément utilisée et le sera de plus en plus pour la flexibilité qu'elle fournit. Dans cet article vous verrez comment utiliser cette authentification de manière « minimaliste » et nous passerons en revue quelques problèmes qui peuvent survenir lors de l'utilisation de ce mode d'authentification…

Pour commencer, il nous faut créer le formulaire d'authentification. Lors du premier accès à l'application, l'absence de cookie dans la requête initiale produira une redirection vers ce formulaire :

Fichier login.aspx

<html>
<head>
<title>Login page</title>
<script language="C#" runat="server">
void Login_Click(Object sender, EventArgs e)
{
if (FormsAuthentication.Authenticate(username.Text, password.Text))
FormsAuthentication.RedirectFromLoginPage(username.Text, false);
// false : non persistent cookie
else status.InnerHtml += "Invalid Login";
}
</script>
</head>
<body>
<span id="status" class="text" runat="Server"/>
<form id="Form1" runat="server">
Username: <asp:textbox id=username cssclass="text" runat="Server"/><br />
Password: <asp:textbox id=password textmode=Password cssclass="text" runat="Server"/><br/>
<asp:button onclick="Login_Click" text=" Login " cssclass="button" runat="Server"/>
</form>
</body>
</html>

Le code de login est très simple : on se contente d'appeler la méthode Authenticate de la classe FormsAuthentication et, si l'authentification est un succès, on redirige l'utilisateur vers la page demandée initialement en appelant RedirectFromLoginPage. L'appel de cette fonction va permettre la génération d'un cookie qui sera envoyé au navigateur et ce dernier « attachera » le cookie dans toute nouvelle requête à destination du site concerné. Le cookie peut être un cookie de session ou un cookie persistant selon le second argument passé à RedirectFromLoginPage (True ou False). Un cookie de session ne possède pas de date d'expiration et est « local » à un processus client (IEXPLORE si le navigateur utilisé est Internet Explorer). Sauf destruction explicite du cookie de session, la durée de vie de ce dernier est donc conditionnée par la durée de vie du processus client. Un cookie persistant sera quand à lui sauvegardé sur disque jusqu'à son expiration. Il pourra donc être utilisé par plusieurs processus.

La configuration de l'authentification par formulaire se fait via l'élément FORM et elle est ici volontairement réduite au strict minimum :

Fichier web.config

<configuration>
<system.web>
<!--membership provider entry goes here-->
<authentication mode="Forms">
<forms loginUrl="Login.aspx">
<credentials passwordFormat="Clear">
<user name="test" password="test" />
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?" />
<allow users="test" />
</authorization>
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="" />
</modules>
</system.webServer>
</configuration>

Pour tester notre application sous IIS7, il suffit de :

  • Créer un répertoire C:\inetpub\wwwroot\formauth
  • Copier les fichiers login.aspx & web.config dans ce répertoire
  • Convertir ce répertoire en application web
  • Autoriser le parcours de fichiers dans le répertoire ou y copier des fichiers de test (default.aspx…etc)
  • S'assurer que la page login.aspx est accessible en authentification anonyme
  • S'assurer que l'application pool est configuré pour utiliser le pipeline intégré

Une navigation sur https://localhost/formauth aura pour effet de faire apparaître le formulaire d'authentification :

Une fois le nom d'utilisateur et mot de passe correct entrés (test/test), le contenu du répertoire sera visible.

Une trace réseau montrera typiquement la séquence suivante :

Requête 

Réponse

Commentaire 

GET /

302 (redirect) sur le formulaire login.aspx 

La requête initiale ne contenant aucun cookie, le serveur redirige le client sur le formulaire d'authentification

GET /formauth/Login.aspx?ReturnUrlxxx

200 OK

Le client récupère le formulaire d'authentification

POST /formauth/Login.aspx?ReturnUrlxxx

302 avec un header :

Set-Cookie: .ASPXAUTH=XXXX

Le client envoie au serveur le formulaire rempli. Le serveur valide les données (utilisateur/mot de passe) et redirige le client vers l'URL demandé initialement en attachant un cookie

GET /

Cookie: .ASPXAUTH=XXX

200 OK

Le client requête l'url initiale en attachant le cookie

GET ….

Cookie: .ASPXAUTH=XXX

 

Toutes les autres requêtes à destination du serveur sont supposées inclure le cookie 

Authentification par formulaire et pipeline

Sous IIS6 (ou IIS7 en pile line classique), l'authentification par formulaire ne sera effective que sur les pages ASPX. IIS7 permet, grâce à son pipeline d'exécution intégré, d'utiliser cette authentification non seulement pour l'accès à des pages ASPX mais également pour tout type d'objet (images, fichiers html, js, etc). Il y a cependant une contrepartie à payer : ce type de configuration ne permet pas de faire à la fois de l'authentification par formulaire et de l'authentification Windows. Une solution à ce problème est fournie ici : IIS 7.0 Two-Level Authentication with Forms Authentication and Windows Authentication

Non prise en compte ou perte de cookie

De nombreux problèmes liés à l'authentification par formulaire sont causés par la non prise en compte (ou la perte) du cookie par le navigateur. Ce type de problème se traduit typiquement par une redirection sur le formulaire d'authentification. Si votre navigateur est Internet Explorer, la première chose à faire consiste à vérifier que le cookie n'est pas rejeté en regardant dans la barre de statut du navigateur :

Dans ce cas de figure, il suffit d'autoriser les cookies pour corriger le problème.

La non prise en compte d'un cookie peut avoir diverses causes dont voici une liste non exhaustive :

- trop grand nombre de cookies (Internet Explorer increases the per-domain cookie limit from 20 to 50)
- nom du serveur utilisant des caractères invalides. Typiquement, Internet Explorer refusera de prendre en compte un cookie si le nom d'un serveur comporte un « _ » (« underscore »)
…etc

La perte d'un cookie peut se produire dans le cas où un cookie de session (non persistant) est utilisé alors que l'application cliente nécessite plusieurs processus pour fonctionner. Le cookie de session étant local à un processus (IEXPLORE.EXE si vous utilisez Internet Explorer), ce cookie n'est en aucun cas « transmis » à une application s'exécutant dans un autre processus. Exemple: The cookie may be lost when a window is opened from a modal or modeless HTML dialog box in Internet Explorer 6. Même en utilisant un cookie persistant, il est possible de rencontrer ce type de problème si le mode protégé d'Internet Explorer 7 est utilisé : Persistent cookies are not shared between Internet Explorer 7 and Office applications in Windows Vista

Si vous êtes intéressés dans les problématiques de perte de cookie, vous pouvez consulter ces articles :

Précisions sur le cookie .ASPXAUTH

Pour des raisons de sécurité, le cookie « .ASPXAUTH » est chiffré et n'est pas accessible par script (cookie « HttpOnly »).

Si plusieurs applications sont hébergées sur un même serveur, il est conseillé d'utiliser un nom de cookie spécifique par application comme indiqué dans cet article : PRB: Forms Authentication Requests Are Not Directed to loginUrl Page.

Tout comme le cookie de session ASP.NET_SessionId, le cookie .ASPXAUTH peut être hébergé « Out- Of-Process » (ASP.NET Session State ou Gestion de l'authentification en Out-Of-Process pour IIS 6). Dans ce cas, un plantage ou un recyclage du processus IIS (W3WP) ne nécessitera pas une nouvelle authentification.

Authentification par formulaire sans cookie

L'authentification par formulaire peut aussi être réalisée sans cookie. Il suffit simplement de définir la propriété cookieless comme suit :

<forms loginUrl="Login.aspx" cookieless="UseUri" >
<credentials passwordFormat="Clear">
<user name="test" password="test" />
</credentials>
</forms>

On pourra utiliser cette option temporairement pour diagnostiquer si un problème est lié à la perte du cookie d'authentification ou non… Pour plus de précision sur cette option, consultez l'article Understand How the ASP.NET Cookieless Feature Works.

Pour aller plus loins…

Notre fichier de configuration utilise une liste statique comprenant un seul utilisateur « test ». Cet utilisateur « test » n'a rien à voir avec un potentiel compte utilisateur « test » déclaré dans Active Directory. Il s'agit ici d'un utilisateur au sens ASP.NET et le contexte d'exécution du code ASPX sera celui défini au niveau de l'identité du pool de l'application (« Network service » par défaut).

Cependant, nous pourrions imaginer différentes variantes à notre petit projet :

  • en lieu et place d'un formulaire, la page de login pourrait être une page accessible via https qui utiliserait un certificat client pour authentifier l'utilisateur
  • nous pourrions « mapper » l'utilisateur authentifié avec un compte Active Directory

De nombreux exemples implémentant ce type de fonctionnalités sont disponibles sur le web :

Pour finir, je ne saurais que trop vous recommander cette excellente série d'articles qui traite en détail de l'authentification par formulaire :

https://www.asp.net/Learn/Security/

Emmanuel Boersma – Equipe de Support IIS France