Création de pages et de contrôles ASP.NET sécurisés
Sur cette page
Dans ce module
Objectifs
S'applique à
Menaces et contre-mesures
Considérations relatives à la conception
Validation de la saisie
Exécution de scripts entre site
Authentification
Autorisations
Emprunt d'identité
Données sensibles
Gestion des sessions
Manipulation des paramètres
Gestion des exceptions
Audit et journalisation
Résumé
Ressources supplémentaires
Dans ce module
Les pages et les contrôles Web constituent la ligne de défense de votre application car ils sont sujets aux attaques visant à compromettre la sécurité de celle-ci et qui sont souvent dirigées contre les systèmes et les magasins de données principaux.
Une attaque, par injection de code ou exécution de code entre sites, par exemple, exploite les vulnérabilités de l'application côté serveur. Ses résultats peuvent être catastrophiques et conduire à la divulgation d'informations, à l'usurpation d'identité, à l'élévation de privilège et à l'exécution de code distant. Pour générer des pages et des contrôles Web sûrs, vous devez suivre les recommandations proposées dans le présent module.
Ce module présente et explique les menaces pesant sur les pages et les contrôles ASP.NET courants ainsi que les contre-mesures appropriées. Il propose ensuite la liste complète des zones de sécurité d'application à renforcer, comme la validation de l'entrée, le codage de la sortie, les authentifications, les autorisations, l'emprunt d'identité, la protection des données sensibles, la gestion de sessions sécurisée, la protection de la manipulation de paramètres et la gestion des exceptions. Ces techniques sont essentielles à une solution solide de sécurité. Souvent négligées, les menaces décrites permettent à des attaquants de compromettre votre système, quel que soit le niveau de sécurité de l'infrastructure.
Objectifs
Ce module vous permettra d'effectuer les opérations suivantes :
-
concevoir des pages et des contrôles ASP.NET sécurisés ;
-
développer du code de validation sûr à l'aide d'expressions rationnelles et d'autres techniques ;
-
empêcher l'exécution de scripts entre sites (XSS) ;
-
authentifier vos utilisateurs et leur attribuer des autorisations ;
-
développer une authentification Forms sûre ;
-
empêcher la transmission au client d'informations détaillées relatives aux exceptions ;
-
gérer et sécuriser vos sessions ASP.NET ;
-
empêcher la manipulation de paramètres ;
-
savoir quelles contre-mesures appliquer pour se protéger des menaces courantes telles que les attaques par injection de code, le détournement de session, l'usurpation d'identité, l'écoute réseau clandestine, la divulgation d'informations, l'exécution de scripts entre sites et la réexécution de cookies.
S'applique à
Ce module s'applique aux produits et technologies suivants :
-
Microsoft® Windows® 2000 Server et Microsoft Windows Server? 2003 ;
-
Microsoft .NET Framework 1.1 et ASP.NET 1.1.
Menaces et contre-mesures
La plupart des attaques d'applications Web transmettent leur code nuisible dans les demandes HTTP. L'objectif est de contraindre l'application à effectuer des opérations interdites ou à provoquer des dysfonctionnements de celle-ci. C'est pourquoi une validation approfondie des données entrantes est une contre-mesure essentielle à la plupart des attaques et doit être privilégiée lors du développement de pages et de contrôles Web ASP.NET. Les menaces les plus courantes sont les suivantes :
-
injection de code ;
-
détournement de session ;
-
usurpation d'identité ;
-
manipulation des paramètres ;
-
écoute clandestine du réseau ;
-
divulgation d'informations.
La figure 10.1 met en évidence les menaces les plus courantes pesant sur les applications Web.
Figure 10.1
Menaces courantes pesant sur les pages et les contrôles Web ASP.NET
Injection de code
L'injection de code se produit quand un attaquant provoque l'exécution de code arbitraire dans le contexte de sécurité de votre application. Le risque augmente si votre application s'exécute à l'aide d'un compte privilégié.
Attaques
Il existe plusieurs types d'attaques par injection de code :
-
Exécution de scripts entre sites. Une application Web reçoit du code nuisible en entrée. Celui-ci est renvoyé au navigateur de l'utilisateur où il est exécuté.
-
Dépassements de capacité de la mémoire tampon. La vérification sécurisée du type du code géré réduit significativement ce risque mais l'application reste vulnérable, en particulier quand elle appelle du code non géré. Les dépassements de capacité de la mémoire tampon peuvent permettre à un attaquant d'exécuter du code arbitraire dans un processus d'application Web, en utilisant son contexte de sécurité.
-
Injection SQL. Cette attaque vise le code d'accès aux données vulnérable. L'attaquant envoie du code SQL entrant qui modifie la requête attendue ou qui exécute des requêtes totalement nouvelles dans la base de données. Les pages de connexion d'authentification de formulaires sont des cibles courantes du fait que le nom d'utilisateur et le mot de passe sont employés pour l'interrogation du magasin d'utilisateurs.
Vulnérabilités
Les vulnérabilités pouvant conduire à la réussite d'attaques par injection de code sont les suivantes :
-
validation d'entrée faible ou inexistante ou confiance dans la validation d'entrée côté client ;
-
inclusion de code entrant non validé dans la sortie HTML ;
-
construction dynamique d'instructions SQL n'utilisant pas de paramètres typés ;
-
utilisation de comptes de processus et de connexions de base de données aux privilèges excessifs.
Contre-mesures
Pour empêcher l'injection de code, optez pour les contre-mesures suivantes :
-
Validez le code entrant de sorte que l'attaquant ne puisse pas injecter de code de script ou provoquer des dépassements de capacité de la mémoire tampon.
-
Cryptez toute sortie incluant une entrée. Ceci évite potentiellement que le navigateur du client interprète comme du code des balises de script nuisibles.
-
Utilisez des procédures stockées acceptant des paramètres pour éviter que la base de données assimile du code SQL entrant nuisible à des instructions exécutables.
-
Utilisez des comptes de processus et d'emprunt d'identité aux privilèges minimaux. Ceci réduit les risques et les dommages résultant de l'exécution de code néfaste dans le contexte de sécurité de l'application.
Détournement de session
Un détournement de session a lieu quand l'attaquant intercepte un jeton d'authentification et prend le contrôle de la session d'un autre utilisateur. Les jetons d'authentification sont souvent stockés dans des cookies ou des URL. En interceptant le jeton d'authentification, l'attaquant peut le transmettre à l'application par l'intermédiaire d'une requête. L'application associe alors la requête à la session de l'utilisateur légitime, permettant ainsi à l'attaquant d'accéder à des zones confidentielles de l'application nécessitant un accès authentifié. Dans ce scénario, l'attaquant exploite l'identité et les privilèges de l'utilisateur légitime.
Vulnérabilités
Le détournement de session est favorisé par les vulnérabilités de pages et de contrôles Web suivantes :
-
identificateurs de session non protégés dans les URL ;
-
mélange de cookies de personnalisation et de cookies d'authentification ;
-
transmission de cookies d'authentification via des liaisons non cryptées ;
Attaques
Les attaques de détournement de session comprennent :
-
La réexécution de cookie. L'attaquant intercepte le cookie d'authentification à l'aide d'un logiciel de contrôle réseau ou par tout autre moyen, par exemple en exploitant la vulnérabilité de l'exécution de scripts entre sites.
-
La manipulation de chaîne de requête. Un utilisateur malintentionné modifie l'identificateur de session affiché en clair dans la chaîne de requête de l'URL.
Contre-mesures
Pour prévenir le détournement de session, vous pouvez opter pour les contre-mesures suivantes, qui consistent à :
-
séparer les cookies de personnalisation et les cookies d'authentification ;
-
transmettre des cookies d'authentification via des connexions HTTPS exclusivement ;
-
éviter de transmettre dans des chaînes de requête des identificateurs de session représentant des utilisateurs authentifiés ;
-
authentifier l'utilisateur avant chaque opération critique, telle qu'une transmission de commande ou un virement bancaire.
Usurpation d'identité
Il y a usurpation d'identité quand un utilisateur malintentionné dérobe l'identité d'un utilisateur légitime pour accéder à l'application.
Vulnérabilités
L'usurpation d'identité est favorisée par les vulnérabilités de pages et de contrôles Web suivantes :
-
transmission d'informations d'authentification via une liaison non cryptée ;
-
transmission de cookies d'authentification via une liaison non cryptée ;
-
mots de passe et stratégies faibles ;
-
stockage non sécurisé des informations d'authentification dans le magasin d'utilisateurs.
Attaques
Les attaques par usurpation d'identité comprennent :
-
La réexécution de cookie. L'attaquant dérobe le cookie d'authentification au moyen d'un logiciel de contrôle réseau ou d'une attaque par exécution de scripts entre sites. Il envoie ensuite le cookie à l'application pour y accéder en usurpant l'identité d'un utilisateur légitime.
-
Les attaques de mot de passe en force. L'attaquant essaie des combinaisons de nom d'utilisateur et de mot de passe de façon répétée.
-
Attaques par dictionnaire. Dans cette forme automatique d'attaques de mots de passe en force, chaque mot du dictionnaire est testé comme mot de passe.
Contre-mesures
Pour prévenir le détournement d'identité, vous pouvez opter pour les contre-mesures suivantes :
-
transmission d'informations d'authentification via des connexions HTTPS exclusivement ;
-
mise en œuvre de mots de passe fiables. Il est possible d'utiliser des expressions rationnelles afin de s'assurer que les mots de passe fournis par les utilisateurs sont conformes aux exigences de complexité du réseau ;
-
stockage de vérificateurs de mots de passe dans la base de données. Stockez des hachages de mots de passe irréversibles combinés à une valeur de salt aléatoire pour réduire le risque d'attaques par dictionnaire.
Pour plus d'informations sur le stockage de hachages de mots de passe et d'informations confidentielles dans la base de données, consultez le module 14, « Création d'un accès sécurisé aux données ».
Manipulation des paramètres
Les paramètres sont des morceaux de données transmis du client au serveur sur le réseau. Ils comprennent des champs de formulaire, des chaînes de requête, l'état de la vue, des cookies et des en-têtes HTTP. En cas de transmission de données sensibles ou de données employées dans le cadre de décisions de sécurité du serveur à l'aide de paramètres non protégés, votre application est exposée à la divulgation d'informations ou aux accès non autorisés.
Vulnérabilités
Les vulnérabilités pouvant conduire à la manipulation de paramètres sont les suivantes :
-
utilisation de champs cachés de formulaire ou de chaînes de requête contenant des données sensibles ;
-
transmission de cookies contenant des données de sécurité sensibles via des connexions non cryptées.
Attaques
Les attaques de manipulation de paramètres sont les suivantes :
-
Attaques par réexécution de cookie. L'attaquant intercepte un cookie, le modifie, puis le renvoie à l'application. Ceci peut aisément conduire à une usurpation d'identité ou à une élévation des privilèges si le cookie contient des données employées pour l'authentification ou l'autorisation sur le serveur.
-
Manipulation de champs cachés de formulaire. Ces champs contiennent des données employées pour la prise de décisions de sécurité sur le serveur.
-
Manipulation de paramètres de chaîne de requête.
Contre-mesures
Pour prévenir la manipulation de paramètres, vous pouvez opter pour les contre-mesures suivantes :
-
Ne faites pas confiance aux options de gestion d'état du client, telles que l'état de la vue, les cookies, les chaînes de requête ou les champs cachés de formulaire employés pour le stockage de données sensibles.
-
Stockez les données sensibles sur le serveur. Utilisez un jeton de session pour associer la session de l'utilisateur aux données sensibles gérées sur le serveur.
-
Utilisez un code d'authentification de message (MAC) pour protéger le jeton de session. Couplez cette opération avec une logique d'authentification, d'autorisation et de pratiques métier garantissant que le jeton n'est pas en cours de réexécution.
Écoute clandestine du réseau
L'écoute clandestine du réseau implique l'utilisation d'un logiciel de contrôle réseau en charge de suivre les paquets de données échangés entre le navigateur et le serveur Web. Ceci peut conduire à la divulgation de données confidentielles de l'application, à la récupération d'informations de connexion et à l'interception de cookies d'authentification.
Vulnérabilités
Les vulnérabilités pouvant conduire à l'écoute clandestine du réseau sont les suivantes :
-
envoi de données sensibles sans cryptage ;
-
envoi de cookies d'authentification via des canaux non cryptés.
Attaques
Les attaques par écoute clandestine du réseau sont réalisées à l'aide de renifleurs de paquets placés sur le réseau afin d'intercepter le trafic.
Contre-mesures
Pour contrer l'écoute clandestine du réseau, optez pour le protocole SSL (Secure Sockets Layer), qui permet d'établir une connexion cryptée entre le navigateur et le serveur Web. Il est impératif d'utiliser SSL lors d'envois d'informations utilisateur, de tickets d'authentification ou de données d'application sensibles sur le réseau.
Divulgation des informations
La divulgation d'informations se produit quand un attaquant teste vos pages Web à la recherche de moyens de générer des conditions d'exception. Il peut s'agir d'un exercice fructueux pour lui car les informations détaillées d'une exception, qui sont généralement renvoyées au format HTML et affichées dans le navigateur, fournissent des renseignements intéressants sur les traces de pile contenant des chaînes de connexion à la base de données, des noms de base de données, des informations sur le schéma de la base de données, des instructions SQL et les versions du système d'exploitation et de la plate-forme.
Vulnérabilités
Les vulnérabilités conduisant à la divulgation d'informations comprennent :
-
les défauts de gestion des exceptions ;
-
la propagation non cryptée d'informations détaillées sur les exceptions au client.
Attaques
Parmi les nombreuses attaques pouvant déboucher sur la divulgation d'informations, on peut citer :
-
les dépassements de capacité de la mémoire tampon ;
-
l'envoi de données en entrée délibérément mal formées.
Contre-mesures
Pour éviter la divulgation d'informations :
-
Recourez à la gestion structurée des exceptions.
-
Renvoyez des pages d'erreur génériques au client.
-
Utilisez des pages de redirection par défaut contenant des messages d'erreur génériques et inoffensifs.
Considérations relatives à la conception
Avant de développer des pages et des contrôles Web, vous devez tenir compte des points importants suivants au moment de la conception :
-
utiliser la validation des données en entrée côté serveur ;
-
partitionner le site Web ;
-
considérer l'identité utilisée pour accéder aux ressources ;
-
protéger les tickets d'identification et d'authentification ;
-
gérer les échecs de façon sûre ;
-
tenir compte de la granularité des autorisations ;
-
placer les contrôles Web et les contrôles utilisateur dans des assemblys séparés ;
-
placer le code d'accès aux ressources dans un assembly séparé.
Utilisation de la validation des données en entrée côté serveur
Lors de la conception, identifiez toutes les sources de données utilisateur en entrée traitées par vos pages et vos contrôles Web. Ceci inclut les champs de formulaire, les chaînes de requête, les cookies émanant de l'utilisateur Web ainsi que les données émanant de sources de données principales. L'utilisateur Web évolue hors de la zone de confiance de votre application, ce qui signifie que les données qui émanent de lui doivent être validées sur le serveur. À moins d'avoir une confiance absolue dans les sources de données principales, il est nécessaire de les valider et de les expurger avant de les adresser au client. Vérifiez que votre solution ne s'appuie pas sur une validation côté client, car celle-ci peut aisément être contournée.
Partition du site Web
Au niveau conception, votre site Web doit distinguer clairement les zones à accès libre et les zones restreintes, nécessitant un accès authentifié. Utilisez des sous-répertoires séparés sous le répertoire racine de votre application destinés à gérer des pages à accès restreint, comme la fonctionnalité de validation d'un site Web de commerce électronique conventionnel qui nécessite un accès authentifié et transmet des données sensibles (des numéros de cartes bancaires, par exemple). La séparation des sous-répertoires permet d'augmenter la sécurité (SSL est requis, par exemple) sans influer sur les performances de SSL sur le site. Elle permet également de réduire le risque de détournement de session en limitant la transmission de cookies d'authentification aux connexions HTTPS. La figure 10.2 représente un partitionnement type.
Figure 10.2
Site Web partitionné en zone publique et zone sécurisée
En examinant la figure 10.2, vous pouvez constater que le sous-dossier à accès restreint est configuré dans Internet Information Services (IIS) pour requérir un accès SSL. Le premier élément <authorization> de Web.config permet à tous les utilisateurs d'accéder à la zone publique, alors que le second empêche les utilisateurs non authentifiés d'accéder au contenu du sous-dossier sécurisé et impose une connexion.
Pour plus d'informations sur la transmission de cookies d'authentification sur connexions HTTPS exclusivement et sur les modalités de navigation entre pages à accès restreint et pages à accès libre, consultez « Utilisation d'URL absolus » dans la section « Authentification » de ce module.
Prise en compte de l'identité employée pour accéder aux ressources
Par défaut, les applications ASP.NET n'empruntent pas d'identité, et le compte de processus ASP.NET aux privilèges minimaux est employé pour exécuter les applications Web ASP.NET et accéder aux ressources. Le comportement par défaut est recommandé. Cependant, dans certaines situations, il peut être nécessaire d'utiliser un contexte de sécurité différent pour accéder aux ressources. Exemple :
-
Hébergement de plusieurs applications sur le même serveur
IIS vous permet de configurer chaque application pour utiliser un compte utilisateur Internet anonyme séparé et activer l'emprunt d'identité. Chaque application possède alors une identité distincte pour l'accès aux ressources. Pour plus d'informations sur cette approche, consultez le module 20, « Hébergement de plusieurs applications Web ». -
Accès à une ressource distante avec des exigences d'authentification spécifiques
Si vous devez accéder à une ressource distante précise (telle qu'un partage de fichiers) et possédez, pour cela, un compte Windows, vous pouvez configurer celui-ci en tant que compte utilisateur Web anonyme pour votre application. Vous pouvez ensuite recourir à l'emprunt d'identité par l'intermédiaire d'un programme pour accéder à la ressource distante. Pour plus d'informations, consultez « Emprunt d'identité », plus loin dans ce module.
Protection des tickets d'identification et d'authentification
Votre conception doit prévoir la protection des tickets d'identification et d'authentification. Les informations d'identification doivent être sécurisées si elles sont acheminées sur le réseau et quand elles résident dans des magasins persistants tels que les fichiers de configuration. Les tickets d'authentification doivent être sécurisés sur le réseau en raison de leur vulnérabilité au détournement d'informations. Le cryptage fournit une solution. SSL ou IPSec permet de protéger les informations utilisateur et les tickets, tandis que DPAPI constitue une bonne solution de cryptage des informations utilisateur dans les fichiers de configuration.
Gestion sécurisée des échecs
Si votre application échoue avec une condition d'exception irrécupérable, vérifiez que cet échec a lieu dans un contexte sûr et ne laisse pas le système ouvert. Vérifiez que les informations détaillées relatives à l'exception susceptibles de servir à un utilisateur malintentionné ne doivent pas se propager au client et sont remplacées par des pages d'erreur génériques. Vous devez envisager d'utiliser une gestion structurée des exceptions au lieu de vous baser sur les codes d'erreur de méthodes.
Prise en compte de la granularité des autorisations
Considérez la granularité des autorisations que vous employez dans les parties authentifiées de votre site. Si vous avez configuré un répertoire pour exiger une authentification, vos utilisateurs doivent-ils disposer d'un accès égal aux pages de ce répertoire ? Au besoin, il est possible d'appliquer des règles d'autorisation différentes pour des pages séparées en fonction de l'identité ou, plus couramment, de l'appartenance du rôle de l'appelant, en utilisant plusieurs éléments <authorization> dans des éléments <location> séparés.
Par exemple, Web.config peut comporter des éléments <allow> et <deny> différents pour deux pages distinctes.
Insertion de contrôles Web et de contrôles utilisateur dans des assemblys séparés
Le fait de placer les contrôles Web et les contrôles utilisateur dans leurs propres assemblys permet de configurer la sécurité de chaque assembly indépendamment, en utilisant la stratégie de sécurité d'accès au code. L'administrateur dispose d'une souplesse supplémentaire. Ceci signifie que vous n'êtes pas contraint d'accorder des autorisations étendues à tous les contrôles simplement pour satisfaire les exigences d'un contrôle.
Insertion du code d'accès aux ressources dans un assembly séparé
Utilisez des assemblys séparés, que vous appellerez à partir de classes de pages au lieu d'intégrer le code d'accès aux ressources dans les gestionnaires d'événements des classe de pages. Ceci assouplit considérablement la stratégie de sécurité d'accès au code et s'avère essentiel pour la génération d'applications Web de niveau de confiance partielle. Pour plus d'informations, consultez le module 9, « Utilisation de la sécurité d'accès au code avec ASP.NET ».
Validation de la saisie
Une confiance non fondée sur le type, la longueur, le format ou les valeurs admises de l'entrée utilisateur présente en soi un risque de sécurité. La validation des données entrantes peut devenir une menace réelle si un attaquant découvre que vos évaluations ne sont pas fondées. L'attaquant peut alors fournir des données d'entrée parfaitement formatées mettant en danger votre application. Une confiance totale envers l'entrée utilisateur est l'une des vulnérabilités les plus courantes et les plus dévastatrices pour les applications Web.
Contraintes et expurgation
Définissez des contraintes pour les valeurs d'entrée et vérifiez-les en effectuant la validation du type, de la longueur, du format et de l'intervalle. Il est parfois nécessaire d'expurger l'entrée afin de la rendre inoffensive. Par exemple, si votre application prend en charge des champs d'entrée à format libre, comme les champs de commentaires, il se peut que vous admettiez certains éléments HTML « sûrs », tels que <b> et <i>, et supprimiez tous les autres éléments HTML. Le tableau suivant récapitule les options disponibles pour les contraintes et l'expurgation de données :
Tableau 10.1 : Options de contraintes et d'expurgation de données
| Configuration requise | Options |
|---|---|
| Vérifications de types | Système de types .NET Framework. Analysez les données de chaîne, convertissez-les en type fort, puis gérez FormatExceptions. |
| Contrôles de longueur | Expressions rationnelles |
| Contrôles de format | Correspondance de motifs à l'aide d'expressions rationnelles |
| Contrôles d'intervalles | Contrôle RangeValidator ASP.NET (prend en charge des données de type devise, date, entier, double et chaîne) |
Expressions rationnelles
Les expressions rationnelles permettent de limiter l'étendue de caractères valides, de supprimer des caractères indésirables et d'effectuer des contrôles sur la longueur et le format. Vous pouvez appliquer des contraintes de format de saisie en définissant les motifs à respecter. ASP.NET fournit le contrôle RegularExpressionValidator et la classe Regex est disponible dans l'espace de noms System.Text.RegularExpressions.
La validation réussit si vous utilisez les contrôles du validateur alors que le contrôle est vide. Pour les champs obligatoires, utilisez un RequiredFieldValidator. L'implémentation de la validation des expressions rationnelles est légèrement différente sur le client et le serveur. Sur le client, la syntaxe des expressions rationnelles du logiciel de développement Microsoft Jscript est employée. Sur le serveur, la syntaxe System.Text.RegularExpressions.Regex est employée, mais, comme la syntaxe JScript des expressions rationnelles est un sous-ensemble de la syntaxe System.Text.RegularExpressions.Regex, il est recommandé d'utiliser la syntaxe JScript si l'on souhaite obtenir les mêmes résultats sur le client et sur le serveur.
Pour plus d'informations sur la gamme complète des contrôles de validation ASP.NET, consultez la documentation .NET Framework.
Contrôle RegularExpressionValidator
Pour valider la saisie de champs de formulaires Web, vous pouvez utiliser le contrôle RegularExpressionValidator. Faites-le glisser dans un formulaire Web et définissez ses propriétés ValidationExpression, ControlToValidate et ErrorMessage.
Vous pouvez définir l'expression de validation à l'aide de la fenêtre de propriétés dans Microsoft Visual Studio® .NET ou définir la propriété de manière dynamique dans le gestionnaire d'événements Page_Load. La seconde approche permet de regrouper toutes les expressions rationnelles de tous les contrôles de la page.
Classe Regex
Si vous employez des contrôles HTML standard sans la propriété runat="server" (ce qui rend inutile l'emploi du contrôle RegularExpressionValidator) ou si vous devez valider l'entrée à partir d'autres sources, telles que des chaînes de requête ou des cookies, vous pouvez intégrer la classe Regex dans la classe de pages ou dans une méthode d'assistance de validation, si possible à l'intérieur d'un assembly séparé. Quelques exemples sont proposés plus loin dans cette section.
Commentaires des expressions rationnelles
Les expressions rationnelles sont plus faciles à comprendre quand la syntaxe suivante est employée et quand chaque composant des expressions est commenté à l'aide du signe #. Pour activer les commentaires, vous devez également indiquer RegexOptions.IgnorePatternWhitespace, qui signifie que les espaces sans séquence d'échappement sont ignorés.
Regex regex = new Regex(@"
^ # ancrage au début
(?=.*\d) # doit contenir au moins un chiffre
(?=.*[a-z]) # doit contenir une minuscule
(?=.*[a-z]) # doit contenir une minuscule
.{8,10} # longueur comprise entre 8 et 10 caractères
$ # ancrage à la fin",
RegexOptions.IgnorePatternWhitespace); Champs texte
Pour valider des champs de texte, tels que des noms, des adresses, des numéros d'identification fiscale, etc., vous pouvez choisir d'employer des expressions rationnelles. Ceci vous permet de :
-
définir des contraintes pour l'intervalle de caractères d'entrée admis ;
-
appliquer des règles de formatage (par exemple, les champs de motifs, tels que les numéros d'identification fiscale ou les codes postaux, exigent des motifs spécifiques de caractères de saisie) ;
-
contrôler des longueurs.
Noms
L'exemple suivant comporte un contrôle RegularExpressionValidator permettant de valider un champ de nom.
<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtName" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="nameRegex"runat="server"
ControlToValidate="txtName"
ValidationExpression="^[a-zA-Z'.`-´\s]{1,40}$"
ErrorMessage="Invalid name">
</asp:regularexpressionvalidator>
</form>
L'expression de validation précédente interdit dans le champ de nom tout autre caractère à l'exception des caractères alphabétiques (de casse indifférenciée), de l'apostrophe (pour des noms comme O'Dell) et le caractère point. La longueur de ce champ est limitée à 40 caractères.
Numéros de sécurité sociale
L'exemple suivant représente le code HTML généré pour un contrôle RegularExpressionValidator permettant de valider le champ de saisie d'un numéro de sécurité sociale américain :
<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtSSN" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="ssnRegex" runat="server"
ErrorMessage="Invalid social security number"
ValidationExpression="\d{3}-\d{2}-\d{4}"
ControlToValidate="txtSSN">
</asp:RegularExpressionValidator>
</form>
L'expression de validation précédente est l'une des expressions standard offertes par Visual Studio .NET. Elle valide le format, le type et la longueur du champ de saisie fourni : trois chiffres suivis d'un tiret, de deux chiffres, d'un tiret et de quatre chiffres.
Si vous n'utilisez pas de contrôles serveur (ce qui exclut les contrôles de validateur) ou si vous devez valider l'entrée émanant de sources autres que des champs de formulaire, la classe System.Text.RegularExpression.Regex peut être insérée dans votre code de méthode. L'exemple suivant illustre la validation du même champ à l'aide de la méthode statique Regex.IsMatch directement dans la classe de page, sans recourir au contrôle de validateur :
if (!Regex.IsMatch(txtSSN.Text, @"^\d{3}-\d{2}-\d{4}$"))
{
// Numéro de sécurité sociale non valide
} Champs de date
Les champs d'entrée ayant un type .NET Framework équivalent peuvent être contrôlés par le système de types .NET Framework. Pour valider une date, par exemple, il est possible de convertir la valeur d'entrée en une variable de type System.DateTime et de gérer les exceptions de format qui en résultent si les données d'entrée sont incompatibles.
try
{
DateTime dt = DateTime.Parse(txtDate.Text).Date;
}
// En cas d'échec de la conversion du type, génération d'une exception FormatException
catch( FormatException ex )
{
// Retourner le message de date non valide à l'appelant
}
Outre des contrôles de format et de type, il se peut que vous deviez effectuer un contrôle d'intervalle sur un champ de date, ce qui est aisément réalisable à l'aide de la variable DateTime, comme suit.
// La gestion des exceptions est omise pour des raisons de mise en page
DateTime dt = DateTime.Parse(txtDate.Text).Date;
// Date du jour ou date antérieure
if ( dt > DateTime.Now.Date )
throw new ArgumentException("La date doit être passée"); Champs numériques
Si vous devez valider des données numériques (un âge, par exemple), effectuez des vérifications de type utilisant le type int. Pour convertir l'entrée texte en valeur entière, il est possible d'utiliser Int32.Parse ou Convert.ToIn32, puis de gérer toute exception FormatException susceptible d'être générée en cas d'erreur de type de données. Exemple :
try
{
int i = Int32.Parse(txtAge.Text);
. . .
}
catch( FormatException )
{
. . .
} Contrôles d'intervalles
Il est parfois nécessaire de vérifier que les données entrées figurent dans un intervalle prédéterminé. Le code suivant comporte un contrôle ASP.NET RangeValidator obligeant à entrer des nombres entiers compris entre 0 et 255. Il comporte également le validateur de champ, RequiredFieldValidator. Sans RequiredFieldValidator, les autres contrôles validateurs acceptent une saisie vide.
<form id="WebForm3" method="post" runat="server">
<asp:TextBox id="txtNumber" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator
id="rangeRegex"
runat="server"
ErrorMessage="Veuillez entrer un nombre entre 0 et 255"
ControlToValidate="txtNumber"
style="LEFT: 10px; POSITION: absolute; TOP: 47px" >
</asp:RequiredFieldValidator>
<asp:RangeValidator
id="RangeValidator1"
runat="server"
ErrorMessage="Veuillez entrer un nombre entre 0 et 255"
ControlToValidate="TextBox1"
Type="Integer"
MinimumValue="0"
MaximumValue="255"
style="LEFT: 10px; POSITION: absolute; TOP: 47px" >
</asp:RangeValidator>
<asp:Button id="Button1" style="LEFT: 10px; POSITION: absolute; TOP: 100px"
runat="server" Text="Button"></asp:Button>
</form>
L'exemple suivant montre comment valider un intervalle à l'aide de la classe Regex :
try
{
// La conversion générera une exception si elle est incorrecte.
int i = Convert.ToInt32(sInput);
if ((0 <= i && i <= 255) == true)
{
// données valides, utiliser le nombre
}
}
catch( FormatException )
{
. . .
}
Expurgation de l'entrée
L'expurgation est une opération consistant à rendre inoffensives des données potentiellement nuisibles. Elle s'avère utile quand l'intervalle de valeurs d'entrée admises ne garantit pas l'innocuité des données entrées. Elle peut consister à supprimer un caractère vide à la fin d'une chaîne fournie par l'utilisateur ou à ajouter une séquence d'échappement à des valeurs pour que celles-ci soient traitées comme des littéraux. Si vous devez expurger une entrée et convertir ou supprimer des caractères d'entrée précis, utilisez Regex.Replace.
Remarque : cette approche permet une défense optimale. Commencez toujours par limiter les valeurs d'entrée au jeu de valeurs considérées comme acceptables.
L'exemple suivant supprime un certain nombre de caractères potentiellement non fiables, comme <>\"'%;()&.
private string SanitizeInput(string input)
{
Regex badCharReplace = new Regex(@"^([<>""'%;()&])$");
string goodChars = badCharReplace.Replace(input, "");
return goodChars;
}
Pour plus d'informations sur l'expurgation de champs d'entrée à format libre, tels que les champs de commentaire, consultez « Expurgation de l'entrée à format libre » sous « Exécution de scripts entre sites », plus loin dans ce module.
Validation de contrôles HTML
Si vous n'employez pas des contrôles serveur ? c'est-à-dire des contrôles avec l'attribut runat="server" ?, mais des contrôles HTML standard, vous devez renoncer aux contrôles validateurs ASP.NET. En revanche, vous pouvez valider le contenu de vos pages Web pages à l'aide d'expressions rationnelles dans le gestionnaire d'événements Page_Load. Exemple :
using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// IsPostBack ne s'applique qu'aux
// formulaires serveur (avec runat="server")
if ( Request.RequestType == "POST" ) // formulaires client<
{
// Valider l'adresse électronique fournie
if( !Regex.Match(Request.Form["email"],
@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$",
RegexOptions.None).Success)
{
// Adresse de messagerie non valide
}
// Valider le nom fourni
if ( !RegEx.Match(Request.Form["name"],
@"^[A-Za-z'\- ]$",
RegexOptions.None).Success)
{
// Nom non valide
}
}
}
Validation de l'entrée utilisée pour les accès aux données
Si vous générez des requêtes SQL dynamiques basées sur l'entrée utilisateur, une attaque par injection SQL peut insérer des commandes SQL néfastes exécutables par la base de données. Dans un scénario type d'accès aux données basé sur le Web, il est possible d'utiliser la stratégie de défense approfondie suivante, consistant à :
-
utiliser des expressions rationnelles afin d'établir des contraintes d'entrée dans votre classe de page ;
-
expurger ou refuser l'entrée (dans le cadre d'une défense accrue, il est possible de supprimer des caractères vides ou tout autre caractère non admis à l'aide d'une méthode d'assistance) ;
-
utiliser des procédures stockées paramétrées pour les accès aux données garantissant des contrôles de type et de longueur pour les données employées dans les requêtes SQL.
Pour plus d'informations sur l'utilisation de paramètres dans les accès aux données et sur l'écriture de code d'accès aux données sécurisé, consultez le module 14, « Création d'un accès sécurisé aux données ».
Validation de l'entrée utilisée pour les E/S de fichiers
Évitez d'écrire du code qui accepte l'entrée de fichier ou de chemin d'accès émanant de l'appelant. Préférez les noms et les emplacements de fichiers fixes lors de la lecture et de l'écriture de données. Ainsi, votre code ne sera ni contraint d'accéder à des fichiers arbitraires, ni vulnérable aux bogues de non-respect des règles de noms.
Si vous devez accepter la saisie de noms de fichiers, deux questions se posent à vous. Le chemin et le nom du fichier résultants constituent-ils un nom valide pour le système de fichiers ? Le chemin d'accès est-il valide dans le contexte de votre application ? Par exemple, se trouve-t-il sous la racine du répertoire virtuel de l'application ?
Pour conformer le nom de fichier, utilisez System.IO.Path.GetFullPath. Pour vérifier que le chemin du fichier est valide dans le contexte de votre application, assurez-vous que la sécurité d'accès au code .NET vous autorise à accorder à votre code l'autorisation FileIOPermission précise pour que celui-ci ne puisse accéder qu'aux fichiers de répertoires précis. Pour plus d'informations, reportez-vous aux sections traitant des entrées-sorties de fichiers dans le module 7, « Création d'assemblys sécurisés » et dans le module 8, « La sécurité d'accès au code en pratique ».
Utilisation de MapPath
Si vous utilisez MapPath pour associer un chemin d'accès virtuel à un chemin d'accès physique sur le serveur, optez pour la surcharge de Request.MapPath qui accepte un paramètre booléen, ce qui vous évite de devoir établir un mappage entre applications, comme suit :
try
{
string mappedPath = Request.MapPath( inputPath.Text,
Request.ApplicationPath, false);
}
catch (HttpException)
{
// Tentative de mappage entre applications
}
Le dernier paramètre false empêche les mappages entre applications. Ceci signifie qu'un utilisateur ne peut pas fournir de chemin contenant « .. » pour quitter la hiérarchie de répertoires virtuels de l'application. Toute tentative pour y parvenir débouche sur une exception de HttpException.
Remarque : les contrôles serveur peuvent lire des fichiers à l'aide de la méthode Control.MapPathSecure. Celle-ci exige que le code appelant bénéficie de la confiance totale accordée par la stratégie de sécurité d'accès au code ; à défaut, une exception HttpException sera générée. Pour plus d'informations, reportez-vous à Control.MapPathSecure dans la documentation du SDK de .NET Framework.
Expressions rationnelles courantes
Visual Studio .NET offre un ensemble d'expressions rationnelles utiles. Pour y accéder, ajoutez un contrôle RegularExpresssionValidator à un formulaire Web et cliquez sur le bouton points de suspension du champ de propriété Expression du contrôle. Le tableau suivant comporte des expressions supplémentaires pour les champs de page Web courants.
Tableau 10.2 : Champs d'expressions rationnelles utiles
| Champ | Expression | Exemples de format | Description |
|---|---|---|---|
| Nom | [a-zA-Z'`-´\s]{1,40} | John DoeO'Dell | Valide un nom. Autorise jusqu'à 40 caractères majuscules ou minuscules et quelques caractères spéciaux propres à certains noms. Il est possible de personnaliser cette liste. |
| Nombres | ^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$ | (425)-555-0123 | Valide un numéro de téléphone américain. |
| Messagerie | \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* | Valide une adresse de messagerie. | |
| URL | ^(http|https|ftp)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*$ |
| Valide une URL. |
| Code postal | ^(\d{5}-\d{4}|\d{5}|\d{9})$|^([a-zA-Z]\d[a-zA-Z] \d[a-zA-Z]\d)$ |
| Valide un code postal américain comprenant cinq ou neuf chiffres. |
| Mot de passe | ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$ |
| Valide un mot de passe sûr comprenant de huit à dix caractères, combinant majuscules, minuscules et chiffres (à l'exception des caractères spéciaux). |
| Entiers positifs | \d+ | 0986 | Valide des entiers supérieurs à zéro. |
| Devise (valeur positive) | "\d+(\.\d\d)?" |
| Valide un montant monétaire positif. Requiert deux chiffres après le point décimal. |
| Devise (valeur positive ou négative) | "(-)?\d+(\.\d\d)?" |
| Valide un montant monétaire positif ou négatif. Requiert deux chiffres après le point décimal. |
Exécution de scripts entre site
Les attaques par exécution de scripts entre sites (ou attaques XSS) exploitent les vulnérabilités de la validation des pages Web en injectant du code de script côté client. Ce code est ensuite renvoyé à un utilisateur qui ignore la manœuvre frauduleuse, puis exécuté par le navigateur. Du fait qu'il télécharge le code de script à partir d'un site approuvé, le navigateur n'a aucun moyen de détecter l'illégitimité du code : les zones de sécurité d'Internet Explorer ne fournissent d'ailleurs aucun moyen de défense contre les attaques XSS. Celles-ci fonctionnent également sur des connexions HTTP ou HTTPS (SSL). Une attaque particulièrement grave se produit quand un attaquant génère un script destiné à récupérer le cookie d'authentification qui fournit un accès au site approuvé et l'envoie à une adresse Web connue de l'attaquant. Ce dernier peut alors usurper l'identité de l'utilisateur légitime et obtenir un accès illicite au site Web.
Pour prévenir les attaques XSS, utilisez les contre-mesures suivantes, consistant à :
-
valider la saisie ;
-
crypter la sortie.
Validation de la saisie
À l'aide des diverses techniques décrites précédemment dans ce module, validez toute entrée reçue hors des limites d'approbation de votre application, que ce soit au niveau du type, de la longueur, du format ou des valeurs admises.
Cryptage de la sortie
Si vous écrivez la sortie de texte dans une page Web sans savoir si le texte ne contient pas de caractères HTML spéciaux (tels que <, > et &), vous devez la pré-traiter à l'aide de la méthode HttpUtility.HtmlEncode. Effectuez ce pré-traitement même si le texte émane d'une entrée de l'utilisateur, d'une base de données ou d'un fichier local. De la même façon, encodez les chaînes d'URL à l'aide de HttpUtility.UrlEncode.
La méthode HtmlEncode remplace les caractères sans signification spéciale dans HTML par des variables HTML les représentant. Par exemple, < est remplacé par < et " est remplacé par ". Les données encodées ne sont pas exécutées par le navigateur, mais traitées comme du code HTML inoffensif.
Response.Write(HttpUtility.HtmlEncode(Request.Form["name"]));
Contrôles liés aux données
Les contrôles Web liés aux données ne codent pas la sortie. Le contrôle TextBox est le seul à coder la sortie quand sa propriété TextMode est réglée sur MultiLine. Si vous liez tout autre contrôle à des données ayant du code XSS nuisible, celui-ci sera exécuté sur le client. Par conséquent, si vous récupérez des données à partir d'une base de données sans être certain de la validité des données (probablement parce que la base de données est partagée avec d'autres applications), codez les données avant de les transmettre au client.
Expurgation de l'entrée à format libre
Si votre page Web comporte une zone de texte à format libre, tel qu'un champ « commentaires », acceptant certains éléments HTML inoffensifs comme <b> et <i>, vous pouvez, par sécurité, effectuez un pré-traitement de l'entrée avec HtmlEncode, puis supprimer de façon sélective le codage des éléments admis. Exemple :
StringBuilder sb = new StringBuilder( HttpUtility.HtmlEncode(userInput) ) ;
sb.Replace("<b>", "<b>");
sb.Replace("</b>", "</b>");
sb.Replace("<i>", "<i>");
sb.Replace("</i>", "</I>");
Response.Write(sb.ToString());
Contre-mesures de défense approfondies
Outre les techniques traitées précédemment, vous pouvez empêcher l'exécution de scripts entre sites à l'aide des contre-mesures suivantes et, en particulier :
-
définir le codage de caractères approprié ;
-
utiliser l'option validateRequest d'ASP.NET 1.1 ;
-
installer URLScan sur le serveur Web ;
-
utiliser l'option de cookie HttpOnly ;
-
utiliser l'attribut de sécurité <frame> ;
-
utiliser la propriété innerText.
Définition du codage de caractères approprié
Pour réduire les critères de validité des données pour vos pages Web, il est important de limiter les façons dont les données d'entrée sont représentées. Les utilisateurs malintentionnés ne peuvent ainsi pas tromper vos routines de validation d'entrée en recourant à la conformation aux règles de noms et aux routines de séquences d'échappement multi-octet.
ASP.NET permet de spécifier le jeu de caractères au niveau page ou au niveau application en utilisant l'élément <globalization> dans Web.config. Les deux approches sont représentées ci-dessous avec le codage de caractères ISO-8859-1, qui est le jeu de caractères par défaut des premières version de HTML et de HTTP.
Pour définir le codage de caractères au niveau page, utilisez l'élément <meta> ou l'attribut de niveau page ResponseEncoding comme suit :
<meta http-equiv="Content Type"
content="text/html; charset=ISO-8859-1" />
OU
<% @ Page ResponseEncoding="ISO-8859-1" %>
Pour définir le codage de caractères dans Web.config, optez pour la configuration suivante :
<configuration>
<system.web>
<globalization
requestEncoding="ISO-8859-1"
responseEncoding="ISO-8859-1"/>
</system.web>
</configuration>
Validation de caractères Unicode
Le code suivant permet de valider des caractères Unicode dans une page :
using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// Le nom doit contenir de 1 à 40 caractères alphanumériques
// ainsi que, le cas échéant, des caractères spéciaux '`´ ; par exemple
// D'Angelo
if (!Regex.IsMatch(Request.Form["name"], @"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
throw new ArgumentException("Paramètre nom non valide");
// Utiliser des expressions rationnelles pour valider d'autres paramètres
. . .
}
Voici l'explication de l'expression rationnelle présentée dans l'exemple :
-
{<name>} indique une classe de caractères Unicode nommée.
-
\p{<name>} désigne tout caractère de la classe de caractères nommés indiquée par {<name>}.
-
{L} effectue une correspondance de gauche à droite.
-
{Lu} effectue une correspondance en majuscules.
-
{Ll} effectue une correspondance en minuscules.
-
{Zs} effectue une correspondance de séparateur et d'espace.
-
{1,40} signifie supérieur ou égal à 1 et inférieur ou égal à 40 caractères.
-
{Mn} désigne des caractères de marquage et tous les caractères sauf les caractères d'espacement.
-
{Zs} effectue une correspondance de séparateur et d'espace.
-
* indique zéro correspondance ou plus.
-
$ signifie l'arrêt de l'examen à cette position.
Utilisation de l'option validateRequest d'ASP.NET
Fonctionnalité de .NET Framework 1.1, l'attribut validateRequest est réglé sur true par défaut dans l'élément <pages> de Machine.config. Il ordonne à ASP.NET de rechercher dans les données émanant du navigateur tout code potentiellement dangereux, comme le code entrant contenant des éléments <script>. ASP.NET traite le code entrant reçu de champs de formulaire HTML, de cookies et de chaînes de requête. .NET Framework 1.0 ne fournit pas de fonctionnalité équivalente, bien que le filtre ISAPI (Internet Server Application Programming Interface) URLScan d'IIS soit capable d'effectuer un traitement similaire. Vous pouvez également appliquer le réglage à chaque page à l'aide de la balise @ Page, comme suit :
<% @ Page validateRequest="True" %>
Installation d'URLScan sur le serveur Web
URLScan est un filtre ISAPI installé lors de l'exécution de l'outil IISLockdown. Il permet de réduire le risque d'attaques XSS en refusant les données d'entrée potentielles dangereuses. Pour plus d'informations sur IISLockdown et URLScan, consultez le module 16, « Sécurisation de votre serveur Web ».
Note : Sur Windows Server 2003, IIS 6.0 possède une fonctionnalité équivalente à URLScan.
Utilisation de l'option de cookie HttpOnly
Internet Explorer 6 Service Pack 1 prend en charge un nouvel attribut de cookie, HttpOnly, qui empêche le script client d'accéder au cookie à partir de la propriété document.cookie. Une chaîne vide est retournée à la place. Le cookie est toujours envoyé au serveur quand l'utilisateur accède au site Web dans le domaine en cours.
Remarque : les navigateurs Web qui ne prennent pas en charge l'attribut de cookie HttpOnly ne tiennent pas compte soit du cookie, soit de l'attribut, ce qui signifie qu'ils sont toujours exposés aux attaques XSS.
La classe System.Net.Cookie ne prend pas encore en charge la propriété HttpOnly. Pour ajouter un attribut HttpOnly au cookie, vous devez utiliser un filtre ISAPI ou, si vous souhaitez une solution de code géré, ajouter le code suivant au gestionnaire d'événements Application_EndRequest dans Global.asax :
protected void Application_EndRequest(Object sender, EventArgs e)
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies)
{
// Définir l'attribut HttpOnly du cookie d'authentification Forms
// Sauter ce contrôle pour définir l'attribut sur tous les cookies de la collection
if (sCookie.Equals(authCookie))
{
// Forcer l'ajout de HttpOnly à l'en-tête du cookie
Response.Cookies[sCookie].Path += ";HttpOnly";
}
}
}
Remarque : dans une prochaine version de .NET Framework, il se peut que la classe Cookie prenne en charge la propriété HttpOnly.
Utilisation de l'attribut de sécurité <frame>
À partir de la version 6, Internet Explorer prend en charge un nouvel attribut, security, avec les éléments <frame> et <iframe>. L'attribut security permet d'appliquer à frame ou à iframe les paramètres de zone de sécurité Internet Explorer des sites restreints propres à l'utilisateur. Par défaut, la zone Sites restreints n'accepte pas l'exécution de scripts. Dans ce cas, l'attribut security doit être réglé sur « restricted », comme indiqué ci-dessous :
<frame security="restricted" src="http://www.somesite.com/somepage.htm"></frame>
Utilisation de la propriété innerText
En cas de création d'une page dont les valeurs d'entrée ne sont pas fiables, préférez la propriété innerText à la propriété innerHTML. La propriété innerText annihile les effets potentiellement néfastes du contenu et empêche l'exécution des scripts.
Authentification
Un processus d'authentification faible augmente les risques d'usurpation d'identité. Des informations de connexion tombant entre les mains d'un utilisateur malintentionné permettent à celui-ci d'usurper l'identité de leur propriétaire et d'accéder à l'application. L'attaquant partage tous les privilèges de l'utilisateur légitime dans l'application. Les informations d'authentification doivent être protégées lors de leur transmission sur le réseau et, tant qu'elles sont persistantes, dans le magasin d'utilisateurs de l'application. Le cookie d'authentification qui représente une identité authentifiée auprès de l'application après la connexion initiale doit également être protégé pour réduire le risque de détournement de session et de réexécution des cookies d'authentification.
Authentification par formulaire
Les menaces d'attaques par détournement de session et de réexécution de cookies pèsent tout particulièrement sur les applications utilisant l'authentification de type Forms. Vous devez être particulièrement vigilant lors de l'interrogation de la base de données employant des informations d'authentification fournies par l'utilisateur pour vous assurer que vous n'êtes pas vulnérable à l'injection de code SQL. De plus, pour prévenir l'usurpation d'identité, vous devez vous assurer que le magasin d'utilisateurs est sécurisé et que des mots de passe forts sont mis en œuvre.
Le fragment suivant représente une configuration d'authentification de type Forms « sécurisée » dans Web.config :
<forms loginUrl="Restricted\login.aspx" Page de connexion dans un dossier protégé SSL
protection="All" Confidentialité et intégrité
requireSSL="true" Empêcher l'envoi du cookie sur http
timeout="10" Durée de vie limitée pour la session
name="AppNameCookie" Nom unique par application
path="/FormsAuth" et chemin
slidingExpiration="true" > Durée de vie de session de type Sliding
</forms>
Pour générer une solution d'authentification Forms sécurisée, vous devez observer les recommandations suivantes, à savoir :
-
partitionner le site Web ;
-
sécuriser les pages à accès restreint avec SSL ;
-
utiliser l'autorisation d'URL ;
-
sécuriser le cookie d'authentification ;
-
utiliser des URL absolues pour la navigation ;
-
utiliser une gestion sûre des informations d'authentification.
Partition du site Web
Dans la conception de votre site, assurez-vous que les pages sécurisées nécessitant un accès authentifié sont placées dans un sous-répertoire séparé des pages accessibles de façon anonyme. La figure 10.3 représente une telle configuration dans la fenêtre Explorateur de solutions de Visual Studio .NET. Vous pouvez constater que la page de connexion Forms est placée dans un sous-répertoire séparé avec d'autres pages à accès restreint.
Figure 10.3
Sous-répertoire de pages à accès restreint nécessitant une authentification
Remarque : si vous utilisez Server.Transfer pour effectuer des transferts d'une page anonyme vers une page sécurisée, .NET Framework 1.1 ou version antérieure n'effectue pas les contrôles d'authentification, ce qui signifie qu'il faut vérifier que le code contenant Server.Transfer n'effectue pas de transfert vers un répertoire sécurisé.
Sécurisation des pages à accès restreint avec SSL
Pour garantir que SSL protège les informations d'authentification envoyées à partir d'un formulaire de connexion et que le cookie d'authentification est transmis à des pages à accès sécurisé lors des demandes suivantes, configurez dans IIS les dossiers sécurisés nécessitant SSL. Ceci définit l'attribut AccessSSL=true pour le dossier dans la métabase IIS. Les demandes de pages des dossiers sécurisés ne réussiront que si https figure dans l'URL de la requête.
Pour pouvoir employer SSL, votre serveur Web doit posséder un certificat de serveur. Pour plus d'informations, consultez « How To: Setup SSL on a Web Server » dans la section « How To » de « Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication », à l'adresse http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp.
Utilisation de l'autorisation d'URL
Pour autoriser les accès anonymes aux pages publiques, utilisez l'élément <authorization> suivant.
<system.web>
<!-- Le dossier racine virtuel contient des pages générales.
Les utilisateurs anonymes peuvent y accéder sans nécessiter
d'authentification SSL. -->
<authorization>
<allow users="*" />
</authorization>
</system.web>
L'élément <authorization> peut être intégré à un élément <location> dans Web.config pour refuser l'accès aux utilisateurs anonymes et forcer la redirection vers la page de connexion indiquée dans l'élément <forms> :
<!-- Le dossier à accès limité n'est ouvert qu'aux utilisateurs authentifiés sous SSL. -->
<location path="Secure" >
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location> Sécurisation du cookie d'authentification
Pour empêcher les attaques par détournement de session et réexécution de cookies, sécurisez le cookie en vous assurant qu'il ne peut être transmis que via des connexions SSL utilisant le protocole HTTPS. Pour réduire davantage les risques, cryptez le cookie avant de l'envoyer au client et limitez sa période de validité. Pour sécuriser le cookie d'authentification :
-
Limitez le cookie d'authentification aux connexions HTTPS.
-
Cryptez le cookie.
-
Limitez la durée de vie du cookie.
-
Définissez une période d'expiration fixe pour le cookie.
-
Optez pour des cookies d'authentification temporaires.
-
Enregistrez les cookies d'authentification et de personnalisation séparément.
-
Utilisez des noms différents pour les cookies et les chemins d'accès.
Limitation du cookie d'authentification aux connexions HTTPS
Les cookies prennent en charge une propriété « sécurisée » qui détermine si les navigateurs doivent ou non les renvoyer au serveur. Quand cette propriété est définie, le navigateur envoie le cookie à une page sécurisée demandée à l'aide d'une URL de type HTTPS.
Avec .NET Framework 1.1, utilisez la propriété requireSSL="true" sur l'élément <forms> comme suit :
<forms loginUrl="Secure\Login.aspx"
requireSSL="true" . . . />
Avec .NET Framework 1.0, définissez la propriété sécurisée manuellement dans le gestionnaire d'événements Application_EndRequest, dans Global.asax à l'aide du code suivant :
protected void Application_EndRequest(Object sender, EventArgs e)
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies)
{
if (sCookie.Equals(authCookie))
{
// Sécuriser le cookie. Les navigateurs enverront le cookie
// uniquement aux pages demandées par https
Response.Cookies[sCookie].Secure = true;
}
}
}
Cryptage du cookie
Cryptez le contenu du cookie même si vous utilisez SSL. Ceci empêche la visualisation ou la modification du cookie et, par conséquent, son détournement dans le cadre d'une attaque XSS. Dans cette éventualité, l'attaquant peut toujours se servir du cookie pour accéder à votre application. Le meilleur moyen de réduire ce risque est d'implémenter les contre-mesures appropriées afin de vous prémunir contre les attaques XSS (voir « Exécution de scripts entre sites » plus haut dans ce module), et de limiter la durée de vie du cookie comme décrit dans la recommandation suivante.
Pour assurer la confidentialité et l'intégrité du cookie, définissez l'attribut protection de l'élément <forms> comme suit :
<forms protection="All" Confidentialité et intégrité
Limitation de la durée de vie du cookie
Limitez la durée de vie du cookie pour réduire la durée pendant laquelle un attaquant peut intercepter le cookie pour accéder à votre application.
<forms timeout="10" Durée de vie réduite du cookie (10 minutes)
Utilisation d'une période d'expiration fixe
Définissez slidingExpiration="false" de l'élément <forms> afin de définir la période d'expiration du cookie, ce qui vous évite de la définir à chaque demande Web. Cette option est essentielle si vous ne protégez pas le cookie via SSL.
Remarque : cette fonctionnalité est disponible dans .NET Framework 1.1.
Cookies d'authentification temporaires
Évitez d'utiliser des cookies d'authentification permanents qui seraient stockés dans le profil de l'utilisateur et pourraient être exploités par un attaquant prenant possession de l'ordinateur de celui-ci. Pour définir un cookie temporaire lors de la création de FormsAuthenticationTicket, entrez ce qui suit :
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(
1, // version
Context.User.Identity.Name, // nom d'utilisateur
DateTime.Now, // heure d'émission
DateTime.Now.AddMinutes(15), // expire toutes les 15 minutes
false, // cookie temporaire
roleStr ); // rôles utilisateur
Stockage séparé des cookies d'authentification et de personnalisation
Choisissez le stockage séparé pour les cookies contenant des préférences utilisateur et des données non sensibles et les cookies d'authentification. Un cookie de personnalisation dérobé peut ne pas représenter de danger pour la sécurité, alors qu'un attaquant peut employer un cookie d'authentification volé pour accéder à votre application.
Utilisation de noms et de chemins distincts pour les cookies
Employez des valeurs uniques pour les attributs name et path de l'élément <forms>. Ceci évite des problèmes potentiels en cas d'hébergement d'applications multiples sur le même serveur. En l'absence de noms distincts, il est possible pour un utilisateur authentifié dans une application d'envoyer une demande à une autre application sans être redirigé vers la page de connexion de cette application.
Pour plus d'informations, consultez l'article 313116 de la Base de connaissances Microsoft, « PRB: Forms Authentication Requests Are Not Directed to loginUrl Page », et l'article 310415, « PRB: Mobile Forms Authentication and Different Web Applications ».
Utilisation d'URL absolues pour la navigation
La navigation entre zone publique et zones sécurisées (c'est-à-dire entre pages HTTP et pages HTTPS) pose un problème de sécurité car une redirection emploie toujours le protocole (HTTPS ou HTTP) de la page en cours, et non pas de la page cible.
Quand un utilisateur se connecte et parcourt des pages dans un répertoire, les liens relatifs tels que « ..\ page.aspx » ou les redirections vers des pages HTTP débouchent sur des pages servies par le protocole https, ce qui pénalise inutilement les performances. Pour y remédier, employez des liens absolus du type « http://servername/appname/publicpage.aspx » pour les redirections depuis une page HTTPS vers une page HTTP.
De même, pour les redirections depuis une page publique vers une page sécurisée (la page de connexion, par exemple), optez pour un chemin HTTPS absolu (« https://servername/appname/secure/login.aspx », par exemple) et non pas pour un chemin relatif (restricted/login.aspx, par exemple). Si votre page Web comporte un bouton de connexion, redirigez l'utilisateur vers la page de connexion sécurisée à l'aide du code suivant.
private void btnLogon_Click( object sender, System.EventArgs e )
{
// Former un chemin absolu à l'aide du nom du serveur et du nom v-dir
string serverName =
HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);
string vdirName = Request.ApplicationPath;
Response.Redirect("https://" + serverName + vdirName +
"/Restricted/Login.aspx");
} Gestion sécurisée des informations d'identification
L'usurpation d'identité est l'une des menaces liées à l'authentification les plus courantes pesant sur votre application. Ceci se produit quand un attaquant accède à l'application sous le nom d'un autre utilisateur. L'un des moyens d'y parvenir est de dérober le cookie de session, bien que ce risque soit considérablement réduit si vous avez sécurisé le cookie d'authentification. En outre, vous devez établir une gestion sécurisée des informations d'identification et un magasin d'utilisateurs sécurisé afin de réduire le risque d'attaques en force des mots de passe, des attaques par dictionnaire et d'attaques par injection SQL.
Pour réduire les risques d'attaques, vous devez de préférence :
-
utiliser des hachages unidirectionnels pour les mots de passe ;
-
utiliser des mots de passe sûrs ;
-
empêcher l'injection de code SQL.
Utilisation de hachages unidirectionnels pour les mots de passe
Si votre magasin d'utilisateurs est SQL Server, stockez les digests de mots de passe unidirectionnels (les valeurs de hachage) avec une valeur salt ajoutée de façon aléatoire. Cette valeur réduit le risque de tentatives de craquage de mots de passe par attaques en force, de type attaques par dictionnaire, par exemple. L'approche des digests signifie que le stockage des mots de passe n'est jamais vraiment réel. En fait, le mot de passe est obtenu auprès de l'utilisateur et validé en recalculant le digest et en le comparant à la valeur stockée.
Utilisation de mots de passe
Les expressions rationnelles permettent de s'assurer que les mots de passe utilisateur sont conformes aux règles des mots de passe sûrs. L'expression rationnelle suivante peut être employée pour s'assurer que les mots de passe comportent un mélange de 8 à 10 caractères alphabétiques (minuscules et majuscules), numériques et spéciaux. Ceci réduit davantage le risque d'attaques par dictionnaire.
private bool IsStrongPassword( string password )
{
} Prévention de l'injection de code SQL
L'authentification de formulaire est particulièrement sujette aux vulnérabilités conduisant aux attaques par injection de code SQL en raison des modalités d'interrogation de la base de données à l'aide d'informations d'identification utilisateur. Pour réduire le risque :
-
Effectuez une validation soigneuse des informations d'identification fournies. À l'aide d'expressions rationnelles, assurez-vous qu'elles ne comportent pas de caractères SQL.
-
Accédez à la base de données du magasin d'utilisateurs à l'aide de procédures stockées paramétrées.
-
Utilisez une connexion restreinte disposant de privilèges minimaux pour accéder à la base de données.
Pour plus d'informations sur la prévention d'injection SQL, consultez le module 14, « Création d'un accès sécurisé aux données ».
Autorisations
Les autorisations permettent de contrôler les accès aux répertoires, aux pages Web individuelles, aux classes de pages et aux méthodes. Si nécessaire, vous pouvez inclure une logique d'autorisation dans le code de validation. Lors de la génération d'autorisations dans vos pages et contrôles Web, vous pouvez :
-
contrôler les accès aux pages et aux répertoires à l'aide d'autorisations d'URL ;
-
utiliser les autorisations de fichiers avec les authentifications Windows ;
-
utiliser des demandes principales sur les classes et les méthodes ;
-
renforcer les autorisations à l'aide de contrôles de rôle explicites.
Contrôle des accès aux pages et aux répertoires à l'aide d'autorisations d'URL
Pour les accès de niveau page et de niveau répertoire, choisissez l'autorisation d'URL, configurée à l'aide de l'élément <authorization>. Pour limiter l'accès à des fichiers ou à des répertoires précis, placez l'élément <authorization> dans un élément <location>.
Pour plus d'informations, reportez-vous à la section consacrée aux autorisations dans le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
Utilisation des autorisations de fichiers avec les authentifications Windows
Si ASP.NET est configuré pour les authentifications Windows, FileAuthorizationModule vérifie les types de fichiers ASP.NET dans toutes les requêtes. Ces types de fichiers comprennent les fichiers de page ASP.NET (.aspx), les contrôles utilisateur (.ascx) et tout autre type de fichier associé au filtre ISAPI d'ASP.NET par IIS.
Pour configurer FileAuthorizationModule, définissez les listes de contrôles d'accès Windows appropriées sur les fichiers ASP.NET.
Utilisation des demandes principales sur les classes et les méthodes
Les demandes d'autorisation principales permettent de prendre des décisions d'autorisation en fonction de l'identité et du rôle de l'appelant. L'identité et le rôle de l'appelant sont gérés par l'objet principal associé à la demande Web en cours (accessible via HttpContext.User). Pour fournir des contrôles d'accès aux classes et méthodes, utilisez des attributs de sécurité déclaratifs, comme suit :
// Syntaxe déclarative
[PrincipalPermission(SecurityAction.Demand,
Role=@"NomDomaine\WindowsGroup")]
public void SomeRestrictedMethod()
{
} Renforcement des autorisations à l'aide de contrôles de rôle explicites
Les contrôles de sécurité déclaratifs empêchent un utilisateur d'accéder à une classe ou d'appeler une méthode précise. Si vous devez intégrer de la logique supplémentaire dans une méthode en vue de prendre des décisions liées aux autorisations, optez pour des demandes d'autorisation principales impératives ou pour des contrôles de rôle à l'aide de IPrincipal.IsInRole. Ces approches permettent d'utiliser des variables d'exécution supplémentaires afin d'adapter plus précisément la décision d'autorisation à vos besoins. L'exemple suivant illustre l'utilisation d'une demande d'autorisation principale impérative :
// Syntaxe impérative
public void SomeRestrictedMethod()
{
// Seuls les appelants membres du groupe Windows indiqué
// bénéficient d'un accès
PrincipalPermission permCheck = new PrincipalPermission(
null, @"NomDomaine\WindowsGroup");
permCheck.Demand();
// Opérations restreintes (section omise)
}
L'exemple suivant illustre l'utilisation de IPrincipal.IsInRole :
public void TransferMoney( string fromAccount,
string toAccount, double amount)
{
// Extraire l'utilisateur authentifié du contexte HTTP courant.
// La variable User est équivalente à HttpContext.Current.User si vous
// utilisez une page .aspx (ou .asmx)
WindowsPrincipal authenticatedUser = User as WindowsPrincipal;
if (null != authenticatedUser)
{
// Remarque : Pour extraire le nom de l'utilisateur authentifié, employez la
// ligne de code suivante
// string username = authenticatedUser.Identity.Name;
// Si le montant dépasse la valeur seuil, l'acceptation du responsable est requise
if (amount > thresholdValue) {
// Contrôler le rôle
if (authenticatedUser.IsInRole(@"NomDomaine\Manager") )
{
// Transfert autorisé
}
else
{
throw new Exception("Transfert bancaire interdit");
}
}
else
{
. . .
}
}
}
Vous pouvez également disposer d'une méthode autorisant des appelants ayant des rôles différents. Cependant, si vous souhaitez appeler ensuite une autre méthode, la sécurité déclarative ne vous le permettra pas.
Emprunt d'identité
Par défaut, les applications ASP.NET n'empruntent pas l'identité de l'appelant d'origine, et ce, pour des raisons de conception, de mise en œuvre et d'évolutivité. Ainsi, l'emprunt d'identité empêche la mise en pool de connexions intermédiaires, ce qui freine sérieusement l'évolutivité des applications.
Dans certains scénarios, il peut être nécessaire d'emprunter une identité (par exemple, si vous avez besoin d'une identité autre qu'une identité de processus pour accéder à des ressources). Dans les environnements d'hébergement, l'isolement des applications est possible grâce à l'emploi de plusieurs identités. Par exemple, si l'application utilise une authentification Forms ou Passport, vous pouvez emprunter l'identité du compte utilisateur Internet anonyme associé au répertoire virtuel de l'application.
Vous pouvez emprunter l'identité de l'appelant d'origine, qui peut être le compte utilisateur Internet anonyme ou une identité fixe. Pour emprunter l'identité de l'appelant d'origine (identité authentifiée IIS), employez la configuration suivante :
<identity impersonate="true" />
Pour emprunter une identité fixe, utilisez les attributs supplémentaires userName et password de l'élément <identity>, sans oublier d'exécuter Aspnet_setreg.exe afin de stocker des informations d'identification cryptées dans le Registre. Pour en savoir plus sur le cryptage d'informations d'identification dans des fichiers de configuration et sur Aspnet_setreg.exe, consultez le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
Emprunt d'identité par programme
Si vous ne souhaitez pas emprunter l'identité d'un compte pour la totalité de la demande, vous pouvez recourir à l'emprunt d'identité par programme et l'appliquer à une partie de la demande. Par exemple, vous pouvez accéder aux ressources principales et à la base de données en aval de l'application à l'aide du compte de processus ASP.NET, et employer une autre identité pour accéder à une autre base de données distante ou à un partage de fichiers distant.
Pour ce faire, utilisez IIS pour configurer le compte utilisateur anonyme comme identité de remplacement approuvée. À l'aide du code suivant, créez un jeton d'emprunt d'identité utilisant le compte anonyme lors de l'exécution du code d'accès aux ressources distantes :
HttpContext context = HttpContext.Current;
// Obtenir le fournisseur de service auprès du contexte
IServiceProvider iServiceProvider = context as IServiceProvider;
//Obtenir un Type représentant un HttpContext
Type httpWorkerRequestType = typeof(HttpWorkerRequest);
// Obtenir le service HttpWorkerRequest auprès du fournisseur de services
// Remarque : Lors de la tentative d'extraction d'un type HttpWorkerRequest auprès d'un HttpContext<
// une autorisation de code non géré est requise.
HttpWorkerRequest httpWorkerRequest =
iServiceProvider.GetService(httpWorkerRequestType) as HttpWorkerRequest;
// Obtenir le jeton transmis par IIS
IntPtr ptrUserToken = httpWorkerRequest.GetUserToken();
// Créer une identité WindowsIdentity à partir du jeton
WindowsIdentity winIdentity = new WindowsIdentity(ptrUserToken);
// Emprunter l'identité de l'utilisateur
Response.Write("Identité empruntée : " +
WindowsIdentity.GetCurrent().Name + "<br>");
WindowsImpersonationContext impContext = winIdentity.Impersonate();
Response.Write("Identité empruntée : " + WindowsIdentity.GetCurrent().Name + "<br>);
// Placer le code d'accès aux ressources ici
// Arrêter l'emprunt d'identité
impContext.Undo();
Response.Write("Après l'emprunt d'identité : " +
WindowsIdentity.GetCurrent().Name + "<br>");
Remarque : cette approche suppose l'utilisation de l'authentification Forms ou Passport. Dans IIS, le répertoire virtuel de l'application est configuré pour prendre en charge les accès anonymes.
Avec ce code, employez la configuration <identity> suivante :
<identity impersonate="false" />
Remarque : le code exige l'autorisation de code non géré SecurityPermission(SecurityPermissionFlag.UnmanagedCode), qui n'est accordée qu'aux applications Web bénéficiant du niveau de confiance totale.
Données sensibles
Les données sensibles comprennent des informations détaillées de configuration d'application (telles que des chaînes de connexion et des informations d'identification de comptes de service) et des données spécifiques aux applications (telles que des numéros de cartes de crédit). Pour réduire les risques lors de la manipulation de données sensibles, vous devez veiller à :
-
ne pas transmettre de données sensibles d'une page à une autre ;
-
éviter les mots de passe en texte brut dans les fichiers de configuration ;
-
utiliser DPAPI au lieu de gérer les clés ;
-
désactiver la mise en cache des résultats de sortie pour les données sensibles.
Isolement des données sensibles après affichage dans une page
Pour stocker des données sensibles, évitez d'utiliser les options de gestion d'état client, telles que l'état de la vue, les cookies, les chaînes de requête ou les variables de champs de formulaire cachés. Les données peuvent être falsifiées et affichées en texte clair. Pour un échange sécurisé des données, choisissez les options de gestion d'état serveur, comme la base de données SQL Server.
Apparence des mots de passe dans les fichiers de configuration
Les éléments <processModel>, <sessionState> et <identity> possèdent des attributs userName et password dans les fichiers Machine.config et Web.config. Évitez de les stocker sous forme de texte clair. Stockez les informations d'identification cryptées dans le Registre à l'aide de l'outil Aspnet_setreg.exe.
Pour plus d'informations sur le cryptage des informations d'identification dans les fichiers de configuration et sur Aspnet_setreg.exe, consultez le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
Utilisation de DPAPI
DPAPI est parfaitement adapté au cryptage d'informations confidentielles telles que les chaînes de connexion et les informations d'identification du compte de service. Si vos pages doivent employer ce type de données de configuration, DPAPI constitue la solution au problème de la gestion des clés.
Pour plus d'informations, voir « Cryptographie » dans le module 7, « Création d'assemblys sécurisés ».
Désactivation du cache de sortie pour les données sensibles
Si votre page contient des données sensibles (mot de passe, numéro de carte de crédit, état de compte...), elle ne doit pas être placée dans le cache. Pour désactiver la mise en cache d'une page précise, employez l'attribut de niveau page suivant :
<%@ Page OutputCache Duration="0" Location="None" VaryByParam="None" %>
Gestion des sessions
La gestion sécurisée des sessions implique deux points importants. Tout d'abord, assurez-vous que le jeton de session ne peut pas être utilisé pour accéder à des pages sensibles effectuant des opérations sécurisées ou pour accéder à des éléments de données sensibles. De plus, si les données de session contiennent des éléments sensibles, vous devez sécuriser les données de session, y compris le magasin de sessions.
Les deux types de jetons suivants sont associés à la gestion de sessions :
-
Jeton de session. Ce jeton est généré automatiquement par ASP.NET si l'état de session est activé, par exemple, en définissant l'attribut mode de l'élément <sessionState> sur InProc, SQLServer ou StateServer.
Remarque : il est possible de substituer la configuration de <sessionState> et de désactiver ou d'activer l'état de session selon les pages à l'aide de l'attribut EnableSessionState de la balise @Page.
-
Jeton d'authentification. Il est généré par des mécanismes d'authentification, tels que l'authentification Forms, pour assurer le suivi d'une session de l'utilisateur authentifié. Avec un jeton d'authentification valide, un utilisateur peut accéder aux parties à accès limité de votre site Web.
Pour sécuriser la gestion des sessions, il est conseillé de :
-
exiger une authentification pour accéder aux pages sensibles ;
-
ne pas faire confiance aux options de gestion d'état client ;
-
ne pas mélanger jetons de session et jetons d'authentification ;
-
utiliser SSL efficacement ;
-
sécuriser les données de session.
Exigence d'authentification pour les pages sensibles
Assurez-vous d'authentifier les utilisateurs avant de les autoriser à accéder aux données sensibles et restreintes de votre site. La sécurisation de l'authentification et la protection du jeton d'authentification avec SSL assurent la sécurité de la session car il est impossible pour un attaquant de détourner un jeton de session et de le réexécuter. Pour franchir les barrières d'autorisation, celui-ci devrait posséder le jeton d'authentification.
Pour plus d'informations sur la sécurisation du jeton d'authentification pour l'authentification Forms, reportez-vous à « Authentification des formulaires », plus haut dans ce module.
Vigilance vis-à-vis des options de gestion d'état client
Pour stocker des données sensibles, évitez d'utiliser les options de gestion d'état client, telles que l'état de la vue, les cookies, les chaînes de requête ou les champs de formulaire cachés. Les informations peuvent être falsifiées et affichées en texte clair. Pour stocker des données sensibles, choisissez les options de gestion d'état serveur, comme une base de données.
Dissociation des jetons de session et des jetons d'authentification
La gestion sécurisée des sessions exige de dissocier les deux types de jetons. Commencez par sécuriser le jeton d'authentification pour être certain qu'un attaquant ne puisse pas l'intercepter et l'utiliser afin d'accéder aux zones restreintes de votre application. Construisez ensuite l'application de sorte que le jeton de session seul ne permette pas d'accéder aux pages ou aux données sensibles. Le jeton de session ne doit être utilisé qu'à des fins de personnalisation ou de gestion de l'état utilisateur entre plusieurs requêtes HTTP. Sans authentification, évitez de conserver des éléments sensibles relatifs à l'état utilisateur.
Utilisation efficace de SSL
Si votre site comporte des zones sécurisées et des zones publiques, protégez les premières avec SSL. Quand un utilisateur navigue entre les zones sécurisées et les zones publiques, le cookie de session généré par ASP.NET (ou l'URL si vous avez activé l'état de session sans cookie) passe de l'une à l'autre, en texte clair, mais le cookie d'authentification n'est jamais transmis sur des connexions HTTP si la propriété Secure du cookie est définie.
Remarque : la propriété Secure d'un cookie d'authentification Forms est définie avec l'attribut requireSSL="true" de l'élément <forms>.
Un attaquant peut obtenir un cookie de session transmis via une session HTTP non cryptée, mais si votre site est correctement conçu et si les pages et les ressources à accès restreint se trouvent dans un répertoire séparé sécurisé, l'attaquant ne pourra utiliser le cookie de session que pour accéder aux pages publiques non sécurisées. Dans ce cas, aucune menace de sécurité ne pèse sur le site car ces pages n'effectuent pas d'opérations sensibles. Quand il tente de réexécuter le jeton de session dans une page sécurisée, l'attaquant est redirigé vers la page de connexion de l'application, du fait qu'il n'existe pas de jeton d'authentification.
Pour plus d'informations sur l'utilisation de la propriété de cookie Secure et sur la création de solutions d'authentification Forms, voir « Authentification des formulaires », plus haut dans ce module.
Sécurisation des données de session
Si les données de session du serveur contiennent des éléments sensibles, les données et le magasin doivent être sécurisés. ASP.NET prend en charge plusieurs modes d'états de session. Pour plus d'informations sur la sécurisation de l'état de session ASP.NET, reportez-vous à la section correspondante dans le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
Manipulation des paramètres
Les paramètres tels que ceux qui se trouvent dans les champs de formulaire, les chaînes de requête, l'état de vue et les cookies, peuvent être manipulés par des attaquants qui souhaitent généralement accéder à des pages restreintes ou à tromper l'application pour effectuer une opération non autorisée.
Par exemple, si un attaquant sait que vous utilisez un mécanisme de jetons d'authentification faible, comme un numéro facile à deviner dans un cookie, il peut créer un autre cookie comportant un numéro différent qui le fera passer pour un utilisateur différent (avec des privilèges).
Pour remédier aux vulnérabilités de la manipulation de paramètres, vous devez, de préférence :
-
protéger l'état de vue avec des MAC ;
-
utiliser Page.ViewStateUserKey pour contrer les attaques liées aux clics simples de souris ;
-
gérer les données sensibles sur le serveur ;
-
valider les paramètres d'entrée.
Protection de l'état de la vue avec des MAC
Si vos pages ou contrôles Web utilisent l'état de la vue pour conserver l'état entre requêtes HTTP, assurez-vous que l'état de la vue est crypté et que l'intégrité est vérifiée à l'aide de MAC. Par défaut, l'attribut enableViewStateMac de l'élément <pages> de Machine.config garantit que l'état de la vue est protégé par un MAC.
<pages buffer="true" enableSessionState="true"
enableViewState="true" enableViewStateMac="true"
autoEventWireup="true" validateRequest="true"/>
Remarque : la directive @Page prend également en charge les attributs précédents, ce qui permet de personnaliser les paramètres page par page.
Quand vous utilisez l'état de la vue par contrôle, par page ou par application, assurez-vous que enableViewStateMac est réglé sur true.
Server.Transfer
Si votre application utilise Server.Transfer comme illustré ci-dessous et définit le second paramètre Booléen facultatif sur true afin de préserver les collections QueryString et Form , la commande va échouer si enableViewStateMac est défini sur true.
Server.Transfer("page2.aspx", true);
Si vous omettez le second paramètre ou que vous le définissez sur false, aucune erreur ne se produira. Pour préserver les collections QueryString et Form au lieu de définir enableViewStateMac sur false, suivez la procédure décrite dans l'article 316920 de la Base de connaissances Microsoft, « PRB: "View State Is Invalid" Error Message When You Use Server.Transfer ».
Pour plus d'informations sur la configuration de l'élément <machineKey> dans le cadre de contrôle du cryptage et de l'intégrité de l'état de la vue, consultez le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
Utilisation de Page.ViewStateUserKey en vue de contrer les attaques par clic simple
Si vous authentifiez les appelants et utilisez l'état de la vue, définissez la propriété Page.ViewStateUserKey du gestionnaire d'événements Page_Init afin de prévenir les attaques par clic simple. Une attaque par clic simple a lieu quand un attaquant crée une page Web (.htm ou .aspx) pré-remplie avec état de la vue. Cet état peut être généré à partir d'une page (un panier électronique comportant 100 articles, par exemple) précédemment créée par l'attaquant. Celui-ci emprunte l'identité d'un utilisateur authentifié en parcourant la page, qu'il envoie alors au serveur sur lequel l'état de la vue est valide. Le serveur ne dispose d'aucun moyen de savoir que l'état de la vue émane de l'attaquant. La validation de l'état de la vue et les MAC ne permettent pas de contrer cette attaque car l'état de la vue est valide et la page s'exécute dans le contexte de sécurité de l'utilisateur.
Pour contrer l'attaque par clic simple, associez une valeur unique à la propriété Page.ViewStateUserKey. Elle doit être propre à chaque utilisateur : il s'agit généralement du nom de l'utilisateur ou d'un identificateur. Quand l'attaquant crée l'état de la vue, la propriété ViewStateUserKey est initialisée avec le nom de celui-ci. La page envoyée ensuite au serveur est initialisée avec le nom de l'attaquant. Par conséquent, le contrôle du MAC de l'état de la vue échoue, ce qui génère une condition d'exception.
Remarque : cette attaque n'est généralement pas une menace pour les pages Web anonymes (sans nom d'utilisateur), car elles ne véhiculent pas de transactions sensibles.
Gestion des données sensibles sur le serveur
Ne faites pas confiance aux paramètres d'entrée, surtout quand ils sont déterminants lors de décisions de sécurité sur le serveur. Évitez également d'utiliser des paramètres en texte clair pour toute forme de données sensibles, mais stockez les données sensibles dans un magasin de sessions sur le serveur et employez un jeton de session pour référencer les éléments de ce magasin. Assurez-vous que l'utilisateur est authentifié de façon sûre et que le jeton d'authentification est sécurisé correctement. Pour plus d'informations, consultez « Gestion des sessions », plus haut dans ce module.
Validation des paramètres d'entrée
Validez tous les paramètres d'entrée émanant de champs de formulaire, de chaînes de requête, de cookies et d'entêtes HTTP. Pour cela, utilisez la classe System.Text.RegularExpressions.Regex. Par exemple, le code suivant illustre l'utilisation de cette classe lors de la validation d'un nom transmis via un paramètre de chaîne de requête. La même technique peut s'appliquer à d'autres formes de paramètres d'entrée, telles que les cookies et les champs de formulaire. Pour valider un paramètre de cookie, par exemple, préférez Request.Cookies à Request.QueryString.
using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// Le nom doit contenir de 1 à 40 caractères alphanumériques
// ainsi que, le cas échéant, des caractères spéciaux '`´ ; par exemple
// D'Angelo
if (!Regex.IsMatch(Request.QueryString["name"],
@"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
throw new Exception("Paramètre nom non valide");
// Utiliser des expressions rationnelles pour valider tous les autres
// paramètres de chaîne de requête
. . .
}
Pour plus d'informations sur l'utilisation d'expressions rationnelles et sur la validation des données d'entrée, reportez-vous à « Validation de l'entrée », plus haut dans ce module.
Gestion des exceptions
Une gestion des exceptions bien conçue empêche la transmission à l'utilisateur des informations détaillées sensibles relatives aux exceptions survenues dans des pages Web. Pour les pages et les contrôles Web ASP.NET, il est conseillé :
-
de retourner des pages d'erreur génériques au client ;
-
d'implémenter des gestionnaires d'erreur de niveau page ou de niveau application.
Pour plus d'informations sur la gestion des exceptions, consultez le module 7, « Création d'assemblys sécurisés ».
Affichage de pages d'erreur génériques sur le client
En cas d'exception non gérée, c'est-à-dire quand une exception se propage à la limite de l'application, retournez une page d'erreur générique à l'utilisateur. Pour cela, configurez l'élément <customErrors> comme suit :
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
La page d'erreur doit inclure un message d'erreur générique adéquat avec, si possible, des informations d'assistance détaillées. Le nom de la page ayant généré l'erreur est transmis à la page d'erreur via le paramètre de requête aspxerrorpath.
Il est également possible d'utiliser plusieurs pages d'erreur pour les différents types d'erreurs. Exemple :
<customErrors mode="On" defaultRedirect="YourErrorPage.htm"> <error statusCode="404" redirect="YourNotFoundPage.htm"/> <error statusCode="500" redirect="YourInternalErrorPage.htm"/> </customErrors>
Pour les pages individuelles, vous pouvez fournir une page d'erreur utilisant l'attribut de niveau page suivant :
<% @ Page ErrorPage="YourErrorPage" %>
Implémentation de gestionnaires d'erreur de niveau page ou de niveau application
Pour intercepter et traiter des exceptions non gérées au niveau page, créez un gestionnaire pour l'événement Page_Error, similaire à celui-ci.
public void Page_Error(object sender,EventArgs e)
{
// Récupérer les informations détaillées sur l'exception source
Exception ex = Server.GetLastError();
// À des fins de diagnostic, écrire les détails dans le journal des événements
. . .
// Empêcher la propagation de l'exception et génération d'un
// événement de niveau application(Application.Error)
Server.ClearError();
}
Si la propagation d'événements est autorisée à partir du gestionnaire de page ou s'il n'y a pas de gestionnaire de page, un événement d'erreur d'application est généré. Pour intercepter des événements de niveau application, implémentez Application_Error dans Global.asax, comme suit :
protected void Application_Error(Object sender, EventArgs e)
{
//Écrire dans le journal.
} Audit et journalisation
L'identité de processus ASP.NET par défaut pour les applications permet d'écrire des enregistrements dans le journal des événements mais ne possède pas les autorisations suffisantes pour créer des sources d'événements. Pour y remédier, vous avez le choix entre créer une classe d'installation, appelée pendant l'installation quand des privilèges d'administration sont disponibles, et configurer les autorisations de la clé de Registre EventLog autorisant l'identité de processus ASP.NET (ou identité empruntée) à créer des sources d'événements durant l'exécution. La seconde approche est recommandée.
-
Pour créer une source d'événement d'application lors de l'installation
-
Cliquez avec le bouton droit de la souris dans la fenêtre Explorateur de solutions de Visual Studio .NET, pointez sur Ajouter, puis cliquez sur Ajouter un composant.
-
Sélectionnez Classe Installer dans la liste des modèles et indiquez un nom de fichier de classe approprié.
Cette opération crée une classe d'installation annotée avec l'attribut RunInstaller(true).
RunInstaller(true) public class EventSourceInstaller : System.Configuration.Install.Installer { . . . } -
Affichez la nouvelle classe d'installation dans la vue Conception, affichez la boîte à outils, puis cliquez sur Composants. Faites glisser un composant EventLogInstaller vers la surface de travail du concepteur.
Remarque : si EventLogInstaller ne figure pas dans la boîte à outils, cliquez avec le bouton droit de la souris dans la boîte à outils, puis cliquez sur Ajouter/Supprimer des éléments. Sélectionnez ensuite EventLogInstaller pour ajouter ce type de composant.
-
Définissez les propriétés EventLogInstaller suivantes :
-
Log. Définissez cette propriété sur « Application », qui correspond au nom du journal d'événements à utiliser. Vous pouvez employer le journal Application par défaut ou un journal spécifique à l'application.
-
Source. Réglez cette propriété sur le nom de la source d'événements. Il s'agit généralement du nom de l'application.
-
-
Générez votre projet et créez une instance de la classe d'installation lors de l'installation.
Les instances de la classe d'installation sont créées et appelées automatiquement si vous utilisez un projet de configuration et de déploiement .NET afin de créer un fichier d'installation Windows (.msi). Si vous utilisez xcopy ou l'outil de déploiement équivalent, utilisez InstallUtil.exe, afin de créer une instance de la classe d'installation et de l'exécuter.
-
Pour confirmer la réussite de la génération de la source d'événements, ouvrez un éditeur de Registre et accédez à :
HKLM\System\CurrentControlSet\Services\EventLog\Application\{nom source}Confirmez que la clé existe et contient une valeur de chaîne EventMessageFile qui pointe vers le fichier de messages d'événement .NET Framework par défaut :
\Windows\Microsoft.NET\Framework\{version}\EventLogMessages.dll
-
Si vous possédez une application existante et ne souhaitez pas créer de classe d'installation, vous devez accorder à l'identité de processus ASP.NET les droits d'accès à la clé du Registre du journal d'événements. Pour en savoir plus sur les informations détaillées sur la clé du Registre et les droits d'accès requis, reportez-vous à « Journal des événements » dans le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
EventLogPermission
La stratégie de sécurité d'accès au code doit accorder l'autorisation EventLogPermission au code qui écrit dans le journal d'événements. Ceci peut poser un problème si votre application Web doit s'exécuter au niveau de confiance partielle. Pour plus d'informations sur l'écriture dans le journal d'événements à partir d'une application Web de confiance partielle, consultez le module 9, « Utilisation de la sécurité d'accès au code avec ASP.NET ».
Résumé
Le présent module a tout d'abord présenté les principales menaces contre lesquelles vous devez protéger les pages et les contrôles que vous développez. La plupart des attaques de niveau application s'appuient sur les vulnérabilités de la validation des données entrées. Vous devez vous assurer que la stratégie de validation est solide et que toutes les données émanant d'un source non approuvée sont validées correctement. Une autre vulnérabilité courante réside au niveau de la protection des cookies d'authentification. La section « Authentification des formulaires » de ce module propose des contre-mesures efficaces contre les accès non autorisés, le détournement de session et la réexécution de cookie de session.
Ressources supplémentaires
Pour plus d'informations, consultez les ressources suivantes :
-
Pour plus d'informations sur la définition d'une configuration Machine.config et Web.config sécurisée, consultez le module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».
-
Pour consulter une liste de contrôle imprimable, reportez-vous à « Liste de contrôle : sécurisation d'ASP.NET », à la section « Listes de contrôle » de ce guide.
-
Pour plus d'informations sur la sécurité de votre poste de développement, consultez « Procédure : sécurisation de la station de travail du développeur » dans la section « Procédure » de ce guide.
-
Pour plus d'informations sur les authentifications et les autorisations dans ASP.NET, consultez le module 8, « ASP.NET Security » dans « Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication », à l'adresse http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetch08.asp.
-
Pour obtenir des informations détaillées sur l'authentification Forms, consultez « Procédure : Use Forms Authentication with SQL Server 2000 » et « How To : Setup SSL on a Web Server » dans la section « How To » de « Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication », à l'adresse http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT00.asp.
-
Pour plus d'informations sur l'utilisation d'expressions rationnelles, consultez l'article 308252 de la Base de connaissances Microsoft, « How to : Match a Pattern by Using Regular Expressions and Visual C# .NET ».
-
Pour plus d'informations sur la validation de l'entrée utilisateur dans ASP.NET, consultez l'article de MSDN intitulé « User Input Validation in ASP.NET », à l'adresse http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/pdc_userinput.asp.
-
Pour plus d'informations sur la propriété de cookie Secure, consultez le RFC2109 sur le site Web du W3C, à l'adresse http://www.w3.org/Protocols/rfc2109/rfc2109.
-
Pour plus d'informations sur les considérations de sécurité émanant de la compétition Open Hack, consultez l'article de MSDN, intitulé « Building and Configuring More Secure Web Sites », à l'adresse http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/openhack.asp.