Exporter (0) Imprimer
Développer tout

Création d'un accès sécurisé aux données

Dernière mise à jour le 31 août 2004
Sur cette page

Dans ce module Dans ce module
Objectifs Objectifs
S'applique à S'applique à
Menaces et contre-mesures Menaces et contre-mesures
Problèmes de conception Problèmes de conception
Validation des entrées Validation des entrées
Injection SQL Injection SQL
Authentification Authentification
Autorisation Autorisation
Gestion de la configuration Gestion de la configuration
Données sensibles Données sensibles
Gestion des exceptions Gestion des exceptions
Création d'un composant sécurisé d'accès aux données Création d'un composant sécurisé d'accès aux données
Considérations sur la sécurité par code d'accès Considérations sur la sécurité par code d'accès
Considérations de déploiement Considérations de déploiement
Résumé Résumé
Informations complémentaires Informations complémentaires

Dans ce module

On entend par accès aux données le processus consistant à accéder à une base de données à partir d'une application Web ASP.NET via l'un des fournisseurs de données ADO.NET disponibles.

Cette base de données est la principale cible des attaques au niveau application. Celles-ci sont utilisées pour exploiter les vulnérabilités de votre code d'accès aux données et obtenir ainsi un accès non autorisé à la base de données. Si tous les autres vecteurs d'attaque sont fermés, la principale ouverture de l'application, le port 80, est un chemin de choix pour l'attaquant qui peut alors voler, manipuler ou détruire les données.

Ce module vous montre comment créer un code d'accès aux données sûr et éviter les vulnérabilités et failles les plus courantes. Il présente une série de contre-mesures et de techniques de défense que vous pourrez utiliser dans votre code d'accès aux données pour endiguer les principales menaces liées à l'accès aux données.

Objectifs

Ce module vous permettra :

  • de concevoir, de créer et de déployer un code sécurisé d'accès aux données.

  • d'utiliser la sécurité d'accès au code et la sécurité basée sur les rôles pour restreindre les accès effectués par des appelants ou par du code non autorisés.

  • d'authentifier les utilisateurs en toute sécurité.

  • d'éviter les attaques par injection SQL.

  • De sécuriser les chaînes de connexion de la base de données.

  • d'utiliser le cryptage pour protéger les données stockées dans la base de données.

  • de protéger les données transmises sur le réseau à partir de la base de données ou vers celle-ci.

  • de stocker de manière sûre les mots de passe (avec hachage et valeur de cryptage aléatoire) dans une base de données.

  • d'implémenter une gestion sûre des exceptions.

  • d'apprendre à utiliser la sécurité d'accès au code pour permettre aux applications Web de confiance partielle d'utiliser les fournisseurs de données OLE DB, Oracle et ODBC (qui exigent une confiance totale).

  • de connaître les contre-mesures à mettre en œuvre pour pallier les menaces courantes d'accès aux données, telles que l'injection SQL, la divulgation des données de configuration, la divulgation des données d'application sensibles, de détails de connexion et de schéma de base de données, les accès non autorisés et l'écoute clandestine du réseau.

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

  • Microsoft SQL Server?

Menaces et contre-mesures

Pour créer un code d'accès aux données sécurisé, vous devez connaître les menaces, savoir où se logent les vulnérabilités dans le code d'accès aux données et apprendre à utiliser les contre-mesures appropriées pour endiguer les risques.

Les principales menaces pour le code d'accès aux données sont les suivantes :

  • Injection SQL

  • Divulgation des données de configuration

  • Divulgation de données d'application sensibles

  • Divulgation du schéma de la base de données et des détails de connexion

  • Accès non autorisé

  • Écoute clandestine du réseau

La figure 14.1 illustre ces principales menaces.

Menaces et attaques du code d'accès aux données

Figure 14.1
Menaces et attaques contre le code d'accès aux données

Injection SQL

Les attaques par injection SQL exploitent le code d'accès aux données vulnérable et permettent à un attaquant d'exécuter arbitrairement des commandes dans la base de données. La menace est d'autant plus grande que l'application utilise un compte non restreint dans la base de données car l'attaquant dispose alors d'une plus grande latitude d'exécution des requêtes et des commandes.

Vulnérabilités

Les vulnérabilités qui rendent votre code d'accès aux données susceptible d'être attaqué par injection SQL sont les suivantes :

  • Validation insuffisante des entrées

  • Construction dynamique d'instructions SQL sans utilisation de paramètres sécurisés

  • Utilisation de connexions sur-privilégiées à la base de données

Contre-mesures

Pour contrer les attaques par injection SQL, veillez à :

  • Restreindre et assainir les données saisies.

  • Utiliser des paramètres SQL sécurisés pour accéder aux données. Ces paramètres peuvent être utilisés avec des procédures stockées ou avec des chaînes de commande SQL créées de manière dynamique. Les paramètres effectuent des vérifications de type et de longueur. Ils s'assurent également que le code injecté est bien traité en tant que données littérales et non en tant qu'instruction exécutable dans la base de données.

  • Utiliser un compte ayant des autorisations restreintes dans la base de données. Idéalement, vous devriez accorder le droit de n'exécuter dans la base de données que certaines procédures stockées et ne pas accorder d'accès direct à la table.

Divulgation des données de configuration

Les données de configuration les plus sensibles utilisées par le code d'accès aux données sont la chaîne de connexion à la base de données. Si une chaîne de connexion interceptée comprend un nom d'utilisateur et un mot de passe, les conséquences peuvent être particulièrement graves.

Vulnérabilités

Les vulnérabilités ci-dessous accroissent le risque de sécurité lié aux données de configuration infectées :

  • Utilisation de l'authentification SQL qui nécessite que les informations d'identification soient spécifiées dans la chaîne de connexion

  • Chaînes de connexion incorporées au code

  • Chaînes de connexion en clair dans les fichiers de configuration

  • Échec du cryptage d'une chaîne de connexion

Contre-mesures

Pour empêcher la divulgation des données de configuration :

  • Utilisez l'authentification Windows afin que les chaînes de connexion ne contiennent pas d'informations d'identification.

  • Cryptez les chaînes de connexion et limitez l'accès aux données cryptées.

Divulgation de données d'application sensibles

De nombreuses applications stockent des données sensibles, telles que les numéros des cartes de crédit de leurs clients. Il est donc essentiel de protéger la confidentialité et l'intégrité de ce type de données.

Vulnérabilités

Les pratiques de codage qui conduisent à la divulgation des données d'application sensibles sont les suivantes :

  • Stockage de données sans cryptage

  • Autorisation faible

  • Cryptage faible

Contre-mesures

Pour empêcher la divulgation des données d'application sensibles :

  • Utilisez un cryptage fiable pour protéger les données.

  • Validez l'autorisation de chaque appelant avant d'accorder l'accès aux données de sorte que les utilisateurs ne puissent visualiser que leurs propres données.

Divulgation du schéma de la base de données et des détails de connexion

Si votre code retourne des détails d'exception au client, un utilisateur malveillant peut utiliser ces informations pour attaquer le serveur. Les exceptions du code d'accès aux données peuvent contenir des données sensibles, telles que les détails de schéma de la base de données, la nature des données stockées ou encore des fragments de code SQL.

Vulnérabilités

Les vulnérabilités suivantes peuvent entraîner la divulgation de données :

  • Gestion inappropriée des exceptions

  • Configuration précaire d'ASP.NET qui autorise le renvoi au client des détails de l'exception non gérée

Contre-mesures

Pour éviter une telle divulgation :

  • Consignez, enregistrez et gérez les exceptions d'accès aux données dans votre code d'accès aux données.

  • Renvoyez des messages d'erreur génériques à l'appelant. Ceci nécessite de configurer de manière appropriée l'élément <customErrors> dans le fichier de configuration Web.config ou Machine.config.

Accès non autorisé

Avec des autorisations inappropriées, les utilisateurs peuvent visualiser les données d'autres utilisateurs ou avoir accès à d'autres données réservées.

Vulnérabilités

Parmi les pratiques facilitant les accès non autorisés, on retient :

  • Le manque d'autorisations dans le code d'accès aux données visant à restreindre l'accès

  • Des comptes de base de données sur-privilégiés

Contre-mesures

Pour empêcher l'accès non autorisé aux données :

  • Utilisez les demandes d'autorisation d'entité pour les utilisateurs appelants.

  • Utilisez les demandes d'autorisation de sécurité d'accès au code pour valider le code appelant.

  • Utilisez les autorisations limitées pour restreindre la connexion de l'application à la base de données et empêcher l'accès direct à la table.

Écoute clandestine du réseau

L'architecture de déploiement de la plupart des applications comprend une séparation physique du code d'accès aux données et du serveur de base de données. Les données sensibles, telles que les données spécifiques d'une application ou les autorisations de connexion à la base de données doivent par conséquent être protégées contre les écoutes clandestines de réseau.

Vulnérabilités

Les pratiques suivantes augmentent la vulnérabilité vis-à-vis de l'écoute clandestine de réseau :

  • Informations d'identification en clair, transmises sur le réseau au cours de l'authentification SQL

  • Données d'application sensibles non cryptées et transmises au serveur de base de données ou à partir de celui-ci

Contre-mesures

Pour limiter la vulnérabilité à l'écoute clandestine :

  • Utilisez l'authentification Windows pour éviter de transmettre des informations d'identification sur le réseau.

  • Installez un certificat de serveur sur le serveur de base de données. Vous obtiendrez ainsi le codage automatique des informations d'identification SQL sur le réseau.

  • Utilisez une connexion SSL entre le serveur Web et le serveur de base de données pour protéger les données sensibles des applications. Ce type de connexion nécessite un certificat de serveur de base de données.

  • Utilisez un canal crypté IPSec entre le serveur Web et le serveur de base de données.

Problèmes de conception

Avant de commencer à écrire le code, vous devez prendre en compte un certain nombre de points relatifs à la conception. Les principaux sont les suivants :

  • Utiliser l'authentification Windows.

  • Utiliser les comptes les moins privilégiés.

  • Utiliser des procédures stockées.

  • Protéger les données sensibles stockées.

  • Utiliser des assemblys d'accès aux données distincts.

Utilisation de l'authentification Windows

Idéalement, votre code doit utiliser l'authentification Windows pour les avantages qu'il procure en matière de sécurité. Avec l'authentification Windows, vous n'avez pas besoin de stocker les chaînes de connexion à la base de données avec les informations d'identification incorporées. Ces informations d'identification ne sont pas transmises par le réseau et vous tirez parti de stratégies de compte et de gestion des mots de passe sûres. Vous devrez toutefois choisir avec soin le compte que vous allez utiliser pour vous connecter à SQL Server avec l'authentification Windows.

Pour plus d'informations, reportez-vous à la rubrique « Authentification », plus loin dans ce module.

Utilisation de comptes les moins privilégiés

Votre application doit utiliser un compte le moins privilégié possible et ayant des droits limités dans la base de données. Veillez à ce que la connexion de l'application à la base de données ait les autorisations et restrictions appropriées. Pour plus d'informations, reportez-vous à la rubrique « Autorisation », plus loin dans ce module.

L'utilisation de comptes moins privilégiés réduit les risques de corruption du compte ou d'injection de code nuisible et en limite les conséquences. En cas d'injection SQL, la commande s'exécute dans le contexte de sécurité défini par le compte de connexion de l'application et est soumise aux autorisations de ce compte dans la base de données. Si vous vous connectez à l'aide d'un compte sur-privilégié, en tant que membre du rôle sysadmin de SQL Server par exemple, l'attaquant peut effectuer toutes les opérations qu'il souhaite sur toutes les bases de données du serveur. Il peut ainsi insérer, mettre à jour ou supprimer des données, effacer des tables ou encore exécuter des commandes du système d'exploitation.

Important : Ne vous connectez pas à SQL Server à l'aide du compte sa ou de tout autre compte membre du rôle sysadmin ou du rôle db_owner de SQL Server.

Utilisation des procédures stockées

Les procédures stockées offrent des avantages en termes de performances, de maintenance et de sécurité. Utilisez les procédures stockées paramétrées pour accéder aux données dès que vous en avez la possibilité. Les avantages en termes de sécurité sont les suivants :

  • Vous pouvez restreindre les connexions de l'application à la base de données de telle sorte qu'elle n'ait l'autorisation d'exécuter que les procédures stockées spécifiées. Il est inutile d'accorder un accès direct à la table. Vous limitez ainsi le risque d'attaque par injection SQL.

  • Des vérifications de longueur et de type sont effectuées sur toutes les données entrées, transmises à la procédure stockée. De même, les paramètres ne peuvent pas être traités en tant que code exécutable. Ici encore, le risque d'injection SQL est réduit.

Si, pour une raison ou une autre, vous ne pouvez pas utiliser des procédures stockées paramétrées et si vous devez créer des instructions SQL de manière dynamique, faites appel à des paramètres typés et à des espaces réservés de paramètres afin de vous assurer que la longueur et le type des données saisies sont vérifiés.

Protection des données sensibles stockées.

Identifiez les données stockées nécessitant la garantie de confidentialité et d'intégrité. Si vous stockez les mots de passe dans la base de données à des fins de vérification uniquement, pensez à utiliser le hachage unidirectionnel. Si la table des mots de passe est infectée, les hachages ne pourront pas être utilisés pour obtenir les mots de passe en clair.

Si vous stockez des données sensibles, fournies par l'utilisateur, telles que les numéros de carte de crédit, utilisez un algorithme de cryptage puissant tel que le Triple DES (3DES) pour crypter vos données. Cryptez la clé de cryptage 3DES à l'aide de l'API de protection des données Win32 (DPAPI) et stockez la clé dans une clé de registre bénéficiant d'une liste de contrôle d'accès (ACL) limitée, dont l'utilisation est réservée aux administrateurs et au compte de traitement de l'application.

Pourquoi ne pas utiliser DPAPI ?

Bien que DPAPI soit recommandée pour le cryptage des chaînes de connexion et autres données secrètes telles que les informations d'identification des comptes qui peuvent être récupérées manuellement et reconstruites en cas de panne de la machine, cette API est moins adaptée au stockage de données telles que les numéros de carte de crédit. Cette inadaptation s'explique par les problèmes de récupération qu'elle pose (si les clés sont perdues, il n'existe aucun moyen de récupérer les données cryptées) et d'utilisation au sein de batteries de serveur Web. Vous devez à la place utiliser un algorithme de cryptage symétrique, tel que 3DES, et crypter la clé de cryptage à l'aide de DPAPI.

Les principales raisons qui expliquent l'inadaptation de DPAPI pour le stockage des données sensibles dans une base de données sont répertoriées ci-dessous :

  • Si vous utilisez DPAPI avec la clé machine et que vous transmettez la chaîne CRYPTPROTECT_LOCAL_MACHINE aux fonctions CryptProtectData et CryptUnprotectData, le compte de la machine génère les clés de cryptage. Ceci signifie que chaque serveur d'une batterie de serveurs Web a une clé différente, qui les empêche d'accéder aux données cryptées par un autre serveur. De plus, si la machine d'un serveur Web est détruite, la clé est perdue et les données cryptées ne peuvent pas être récupérées dans la base de données.

  • Si vous utilisez l'approche de clé machine, tout utilisateur de la machine peut décrypter les données (à moins que vous n'associiez d'autres mécanismes de cryptage).

  • Si vous utilisez DPAPI avec une clé utilisateur et des comptes utilisateurs locaux, chaque compte local de chaque serveur Web a son propre identifiant de sécurité (SID) et une clé différente est générée. Par conséquent, un serveur ne peut pas accéder aux données cryptées par un autre serveur.

  • Si vous utilisez DPAPI avec une clé utilisateur et un profil d'utilisateur nomade sur plusieurs machines de la batterie de serveurs Web, toutes les données partageront la même clé de cryptage/décryptage. Toutefois, si le contrôleur de domaine responsable du compte de l'utilisateur nomade est endommagé ou détruit, il sera impossible de recréer un compte utilisateur avec les mêmes identifiants de sécurité et vous ne pourrez pas récupérer les données dans la base de données.

    En outre, avec un profil utilisateur nomade, si quelqu'un parvient à récupérer les données, celles-ci pourront être décryptées sur n'importe quelle machine du réseau, si l'attaquant peut exécuter le code sous le compte utilisateur concerné. Ceci augmente la zone d'attaques potentielle et n'est donc pas recommandé.

Utilisation d'assemblys d'accès aux données distincts

Si vous en avez la possibilité, évitez de placer la logique d'accès aux données directement dans des pages ASP.NET ou dans des fichiers code-behind. Placer la logique d'accès aux données dans un assembly distinct et implémenter une couche logique d'accès aux données distincte de la logique de présentation et de la logique métier de l'application présente des avantages en termes de maintenance, de réutilisation et de sécurité.

Du point de vue de la sécurité, vous pouvez :

  • Utiliser un nom fort pour l'assembly afin de bénéficier d'une protection inviolable.

  • Utiliser la mise en sandbox pour isoler votre code d'accès aux données, qui est important si votre code doit prendre en charge les appelants de confiance partielle, tels que les applications Web de confiance partielle.

  • Utiliser les méthodes d'accès aux données et classes qui autorisent l'appel de code à l'aide de demandes d'autorisation d'identité.

Pour une protection approfondie, exécutez les autorisations relatives à l'entité principale à l'aide des demandes d'autorisation de l'entité principale dans vos composants métier et utilisez les demandes d'autorisation d'identité relatives au code pour autoriser l'accès au code qui appelle votre logique d'accès aux données, comme l'illustre la figure 14.2.

Séparation de couches de présentation, métier et d'accès aux données

Figure 14.2
Séparation de couches de présentation, métier et d'accès aux données

Pour plus d'informations sur l'autorisation du code d'accès aux données, reportez-vous à la rubrique « Autorisation », plus loin dans ce module.

Validation des entrées

Outre les nécessités économiques de maintien de la validité de vos bases de données et de cohérence de vos données, vous devez valider les données avant de les soumettre à la base de données afin d'éviter l'injection SQL. Si votre code d'accès aux données reçoit des valeurs issues d'autres composants au sein de la limite sécurisée et que vous savez que les données ont déjà été validées (par une page Web ASP.NET ou par un composant métier, par exemple), votre code d'accès aux données peut se passer de la validation de données. Vous devez toutefois veiller à utiliser des paramètres SQL dans votre code d'accès aux données. Ces paramètres valident la longueur et le type des paramètres d'entrée. La section qui suit traite de l'utilisation des paramètres SQL.

Injection SQL

Les attaques par injection SQL peuvent se produire lorsque votre application utilise les valeurs saisies pour créer des instructions SQL dynamiques afin d'accéder à la base de données. Elles peuvent également avoir lieu si votre code utilise des procédures stockées transmises par des chaînes contenant des entrées utilisateurs non filtrées. L'injection SQL peut avoir comme conséquence le fait que les attaquants soient capables d'exécuter des commandes dans la base de données en utilisant la connexion de l'application. Ce problème est accentué si l'application utilise un compte sur-privilégié pour se connecter à la base de données.

Remarque : les mesures conventionnelles de sécurité, telles que l'utilisation de SSL et d'IPSec, ne vous protègent pas des attaques par injection SQL.

Prévention de l'injection SQL

Utilisez les contre-mesures qui suivent pour empêcher les attaques par injection SQL :

  • Restreindre la saisie.

  • Utiliser des paramètres SQL de type sécurisé.

Restriction de la saisie

Validez le type, la longueur, le format et la plage des données saisies. Si vous n'attendez pas de valeurs numériques, ne les acceptez pas. Tenez compte de l'origine des données entrées. Si elles proviennent d'une source de confiance qui est passée par un processus d'approbation, vous pouvez choisir de négliger la validation de données dans votre code d'accès aux données. Si en revanche les données proviennent d'une source non fiable ou si vous souhaitez assurer une protection accrue, les composants et méthodes d'accès aux données doivent valider les données saisies.

Utilisation de paramètres SQL de type sécurisé

La collection Parameters de SQL fournit une vérification de type et une validation de longueur. Si vous utilisez cette collection Parameters, les valeurs entrées sont traitées comme des valeurs littérales et SQL ne les considère pas comme du code exécutable. Autre avantage de la collection Parameters : vous pouvez renforcer les vérifications de type et de longueur. Les valeurs hors plage déclenchent une exception. Cette collection est un bon exemple de défense en profondeur.

Important : SQL ne vous protège pas de l'injection SQL. Toute application accédant à la base de données sans validation appropriée des valeurs saisies ni technique d'accès aux données adaptée est sujette aux attaques par injection SQL.

Utilisez les procédures stockées dès que vous le pouvez et appelez-les à l'aide de la collection Parameters.

Utilisation de la collection Parameters avec les procédures stockées

L'exemple de code qui suit illustre l'utilisation de la collection Parameters :

SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", conn);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
                       "@au_id", SqlDbType.VarChar, 11);
parm.Value = Login.Text;

Ici, le paramètre @au_id est traité comme une valeur littérale et non comme du code exécutable. En outre, la longueur et le type du paramètre sont vérifiés. Dans l'exemple ci-dessus, la valeur saisie ne peut pas dépasser 11 caractères. Si les données ne sont pas conformes au type et à la longueur définis par le paramètre, une exception est générée.

Notez que l'utilisation de procédures stockées n'empêche pas nécessairement l'injection SQL. L'important est d'utiliser les paramètres avec des procédures stockées. Si vous n'utilisez pas de paramètres, vos procédures stockées peuvent être sujettes à injection SQL si elles utilisent des valeurs d'entrée non filtrées. Ainsi, le code qui suit est vulnérable :

SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" + 
                               Login.Text + "'", conn);

Important Si vous utilisez des procédures stockées, veillez à bien utiliser des paramètres.

Utilisation de la collection Parameters avec SQL dynamique

Si vous ne pouvez pas utiliser de procédures stockées, vous pouvez continuer d'utiliser les paramètres comme l'illustre le code ci-dessous :

SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", conn);
SqlParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id", 
                        SqlDbType.VarChar, 11);
parm.Value = Login.Text;

Utilisation du paramétrage par lot

On pense souvent, à tort, que la concaténation de plusieurs instructions SQL pour envoyer un lot d'instructions au serveur en une seule boucle ne permet pas d'utiliser les paramètres. Vous pouvez pourtant utiliser cette technique si vous vous assurez que les noms des paramètres ne sont pas répétés. Pour ce faire, vous pouvez simplement ajouter un nombre ou toute autre valeur unique à chaque nom de paramètre lors de la concaténation du texte SQL.

Utilisation des routines de filtrage

Une autre approche de protection contre les attaques par injection SQL consiste à développer des routines de filtrage qui ajoutent des caractères d'échappement aux caractères ayant une signification particulière pour SQL, tel que l'apostrophe simple. L'extrait de code qui suit illustre une routine de filtrage qui ajoute un caractère d'échappement :

private string SafeSqlLiteral(string inputSQL)
{
  return inputSQL.Replace("'", "''");
}

Le problème des routines telles que celle-ci et la raison pour laquelle vous ne devez pas vous y fier totalement est qu'un attaquant peut utiliser les caractères ASCII hexadécimaux pour passer outre vos vérifications. En revanche, vous devez filtrer les entrées dans le cadre d'une stratégie globale de protection.

Remarque : ne vous fiez pas au filtrage des entrées.

Utilisation de clauses LIKE

Notez que si vous utilisez une clause LIKE, les caractères génériques devront être accompagnés d'un caractère d'échappement. L'extrait de code qui suit illustre cette technique :

s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");

Authentification

Lorsque votre application se connecte à une base de données SQL Server, vous pouvez choisir entre une authentification Windows et une authentification SQL. L'authentification Windows est la plus sûre. Si vous devez utiliser l'authentification SQL parce que vous devez vous connecter à la base de données à l'aide d'un certain nombre de comptes différents, par exemple, et que vous voulez éviter d'appeler LogonUser, prenez les mesures suivantes pour limiter les risques autant que possible.

Remarque : l'utilisation de LogonUser pour créer un jeton d'emprunt d'identité requiert le privilège élevé « Se comporter comme partie intégrante du système d'exploitation » sur Microsoft Windows 2000. Il convient donc d'éviter d'utiliser cette approche autant que possible.

Tenez compte des recommandations suivantes :

  • Utilisez l'authentification Windows.

  • Protégez les informations d'identification pour l'authentification SQL.

  • Connectez-vous à l'aide d'un compte moins privilégié possible.

Utilisation de l'authentification Windows

L'authentification Windows n'envoie pas d'informations d'identification sur le réseau. Si vous utilisez l'authentification Windows pour une application Web, vous utilisez, dans la majorité des cas, un compte de service ou un compte de processus, tel qu'un compte ASPNET, pour vous connecter à la base de données. Windows et SQL Server doivent tous deux reconnaître le compte que vous utilisez sur le serveur de base de données. Ce compte doit disposer d'une connexion à SQL Server et cette connexion doit être associée à des autorisations pour accéder à la base de données.

Lorsque vous utilisez l'authentification Windows, vous utilisez une connexion approuvée. Les extraits de code qui suivent montrent des chaînes de connexion type qui utilisent l'authentification Windows.

L'exemple ci-dessous utilise le fournisseur de données ADO.NET pour SQL Server :

SqlConnection pubsConn = new SqlConnection(
   "server=dbserver; database=pubs; Integrated Security=SSPI;");

L'exemple ci-dessous utilise le fournisseur de données ADO.NET pour les sources de données OLE DB :

OleDbConnection pubsConn = new OleDbConnection(
   "Provider=SQLOLEDB; Data Source=dbserver; Integrated Security=SSPI;" +
   "Initial Catalog=northwind");

Protection des informations d'identification pour l'authentification SQL

Si vous devez utiliser l'authentification SQL, veillez à ce que les informations d'identification ne soient pas envoyées sur le réseau en clair et cryptez la chaîne de connexion à la base de données car elle contient des informations d'identification.

Pour activer SQL Server de sorte qu'il crypte automatiquement les informations d'identification envoyées sur le réseau, installez un certificat de serveur sur le serveur de base de données. Vous pouvez également utiliser le canal crypté IPSec entre le serveur Web et le serveur de base de données pour sécuriser tout le trafic en provenance ou à destination du serveur de base de données. Pour protéger la chaîne de connexion, utilisez DPAPI. Pour plus d'informations, reportez-vous à la rubrique « Sécurisation de votre chaîne de connexion », de la section Gestion de la configuration, plus loin dans ce module.

Connexion à l'aide du compte le moins privilégié

Votre application doit se connecter à la base de données à l'aide d'un compte le moins privilégié possible. Si vous utilisez l'authentification Windows pour vous connecter, le compte Windows doit être le moins privilégié du système d'exploitation, avoir peu de droits et des capacités restreintes d'accès aux ressources Windows. De plus, que vous utilisiez l'authentification Windows ou l'authentification SQL, la connexion SQL Server correspondante doit avoir des droits restreints dans la base de données.

Pour plus d'informations sur la création d'un compte de base de données peu privilégié et des options de connexion d'une application Web ASP.NET à une base de données distante à l'aide de l'authentification Windows, reportez-vous à la section « Accès aux données » du module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».

Autorisation

Le processus d'autorisation établit si un utilisateur peut récupérer et manipuler des données spécifiques. Il existe deux approches : votre code d'accès aux données peut utiliser l'autorisation pour déterminer si l'opération demandée doit être exécutée ou non et la base de données peut exécuter l'autorisation pour limiter les capacités de la connexion SQL utilisée par votre application.

Avec une autorisation inappropriée, un utilisateur peut être à même de voir les données d'un autre utilisateur et un utilisateur non autorisé peut accéder à des données protégées. Pour pallier ces menaces :

  • Limitez les appelants non autorisés.

  • Limitez le code non autorisé.

  • Limitez la portée de l'application dans la base de données.

La figure 14.3 résume les points d'autorisation et les techniques à utiliser.

Autorisation d'accès aux données, à l'assembly et à la base de données

Figure 14.3
Autorisation d'accès aux données, à l'assembly et à la base de données

Notez la façon dont le code d'accès aux données peut utiliser les demandes d'autorisation pour autoriser l'utilisateur ou le code appelant. Les demandes d'identité de code sont une fonction de sécurité de l'accès au code .NET.

Pour les autorisations de l'application dans la base de données, utilisez une connexion SQL Server la moins privilégiée possible et disposant du droit d'exécuter uniquement les procédures stockées sélectionnées. Sauf indication contraire, l'application ne doit pas être autorisée à effectuer des opérations de création, récupération, mise à jour, destruction/suppression directement sur quelque table que ce soit.

Remarque : les procédures stockées s'exécutent dans le contexte de sécurité du système de base de données. Bien que vous puissiez limiter les opérations logiques d'une application en lui attribuant des autorisations sur certaines procédures stockées uniquement, vous ne pouvez pas limiter les conséquences des opérations exécutées par la procédure stockée. Les procédures stockées sont du code approuvé. Les interfaces avec les procédures stockées doivent être sécurisées via les droits de la base de données.

Limitation des appelants non autorisés.

Vous devez autoriser l'accès à la base des utilisateurs en fonction de leur rôle ou de leur identité avant qu'ils ne se connectent à la base. Les vérifications de rôle ont généralement lieu dans la logique métier de votre application, mais si la séparation entre la logique d'accès aux données et la logique métier n'est pas claire, utilisez les demandes d'autorisation vis-à-vis de l'entité principale pour les méthodes ayant accès à la base de données.

L'attribut suivant garantit que seuls les utilisateurs membres du rôle Manager peuvent appeler la méthode DisplayCustomerInfo :

[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DisplayCustomerInfo(int CustId)
{
}

Si vous souhaitez utiliser un niveau de granularité supplémentaire pour les autorisations et que vous devez exécuter la logique de rôle au sein de la méthode d'accès aux données, utilisez les demandes d'autorisation impératives sur l'entité principale ou la vérification explicite de rôles comme l'indique l'extrait de code qui suit :

using System.Security;
using System.Security.Permissions;

public void DisplayCustomerInfo(int CustId)
{
  try
  {
    //Vérification des autorisations du rôle impératives pour s'assurer
    //que l'appelant est bien manager
    PrincipalPermission principalPerm = new PrincipalPermission(
                                                   null, "Manager");
    //Le code qui suit est exécuté uniquement si l'appelant est
    // membre du rôle "Manager"
  }
  catch( SecurityException ex )
  {
   . . .
  }
}

Le code qui suit utilise une vérification de rôle explicite par programmation pour s'assurer que l'appelant est membre du rôle Manager :

public void DisplayCustomerInfo(int CustId)
{
  if(!Thread.CurrentPrincipal.IsInRole("Manager"))
  {
    . . .
  }
}

Limitation du code non autorisé

En utilisant la protection d'accès au code .NET Framework, et plus spécifiquement les demandes d'identité de code, vous pouvez limiter les assemblys pouvant accéder aux classes et méthodes d'accès aux données.

Si, par exemple, vous souhaitez que le code ne soit écrit que par votre société ou qu'une société de développement spécifique puisse utiliser vos composants d'accès aux données, utilisez StrongNameIdentityPermission et exigez que les assemblys appelants aient un nom fiable, associé à une clé publique spécifique comme indiqué dans l'extrait de code suivant :

using System.Security.Permissions;
. . .
[StrongNameIdentityPermission(SecurityAction.LinkDemand, 
                              PublicKey="002...4c6")]
public void GetCustomerInfo(int CustId)
{
}

Pour extraire la représentation du texte de la clé publique pour un assembly donné, utilisez la commande qui suit :

sn -Tp assembly.dll

Remarque : utilisez un "T" en majuscule dans le commutateur –Tp.

Comme les assemblys d'application Web sont compilés de manière dynamique, vous ne pouvez pas utiliser de noms fiables dans ce cas. Il est alors difficile de limiter l'utilisation d'un assembly d'accès aux données à une application Web spécifique. La meilleure approche consiste à développer une autorisation personnalisée et à exiger cette autorisation de la part du composant d'accès aux données. Les applications Web de confiance totale (ou tout code de confiance totale) peuvent appeler votre composant. Le code de confiance partielle peut, cependant, appeler votre composant d'accès aux données uniquement s'il a reçu une autorisation personnalisée.

Pour obtenir un exemple d'implémentation de l'autorisation personnalisée, reportez-vous à la procédure « Procédure : création d'une autorisation de cryptage personnalisée » dans la section « Procédures » de ce guide.

Limitation de la portée de l'application dans la base de données

L'approche recommandée consiste à créer une connexion SQL Server pour le compte Windows qu'utilise l'application pour se connecter à la base de données. Il suffit ensuite de mapper la connexion SQL Server sur un utilisateur de la base, dans votre base de données. Placez alors l'utilisateur de la base de données dans un rôle défini par l'utilisateur et accordez les autorisations appropriées à ce rôle. Idéalement, vous ne devriez accorder au rôle qu'un accès pour exécuter les procédures stockées utilisées par l'application.

Pour plus de détails sur la configuration de cette approche, reportez-vous à la rubrique « Configuration de l'accès aux données de votre application ASP.NET » du module 19, « Sécurisation de votre application ASP.NET et de vos services Web ».

Gestion de la configuration

Les chaînes de connexion à la base de données constituent le principal problème du code d'accès aux données. Vérifiez avec soin l'emplacement de stockage de ces chaînes et la façon dont elles sont sécurisées, en particulier si elles contiennent des informations d'identification. Pour améliorer la sécurité de votre gestion du cryptage :

  • Utilisez l'authentification Windows.

  • Sécurisez vos chaînes de connexion.

  • Sécurisez les fichiers UDL avec des listes de contrôle d'accès restreintes.

Utilisation de l'authentification Windows

Lorsque vous utilisez l'authentification Windows, les informations d'identification sont gérées pour vous et ne sont pas transmises sur le réseau. Vous évitez également d'incorporer les noms d'utilisateur et les mots de passe dans les chaînes de connexion.

Sécurisation de vos chaînes de connexion

Si vous avez besoin d'utiliser l'authentification SQL, votre connexion contient le nom d'utilisateur et le mot de passe. Si un attaquant exploite la vulnérabilité de divulgation du code source sur un serveur Web ou parvient à se connecter au serveur, il peut récupérer les chaînes de connexion. De même, toute personne disposant d'une connexion légitime sur le serveur peut les afficher. Sécurisez les chaînes de connexion à l'aide du cryptage.

Cryptage de la chaîne de connexion

Cryptez les chaînes de connexion à l'aide de DPAPI. Avec le cryptage DPAPI, vous évitez les problèmes de gestion de clé de cryptage car la clé est gérée par la plate-forme et est liée à un ordinateur ou à un compte utilisateur Windows spécifique. Pour utiliser DPAPI, vous devez appeler les fonctions DPAPI Win32 via P/Invoke.

Pour plus de détails sur la création d'une classe wrapper gérée, consultez la rubrique « How To: Create a DPAPI Library » de la section « How To » dans l'article « 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.

Stockage sécurisé des chaînes de connexion cryptées

Les chaînes de connexion cryptées peuvent être placées dans le registre ou dans le fichier Web.config ou Machine.config. Si vous utilisez une clé sous HKEY_LOCAL_MACHINE, appliquez-lui la liste de contrôle d'accès (ACL) suivante :

Administrateurs : Contrôle total
Compte du processus : Lire

Remarque : le compte de processus est déterminé par le processus dans lequel s'exécute votre assembly d'accès aux données. Il s'agit généralement du processus ASP.NET ou d'un processus de serveur Enterprise Services si votre solution utilise des services Enterprise Services de niveau intermédiaire.

Vous pouvez également envisager d'utiliser HKEY_CURRENT_USER, qui fournit un accès limité. Pour plus d'informations, reportez-vous à la section « Registre » du module 7, « Création d'assemblys sécurisés ».

Remarque : si vous utilisez l'Assistant de connexion à la base de données .NET de Microsoft Visual Studio®, les chaînes de connexion sont stockées comme valeur de propriété en clair dans le fichier d'application Web en code-behind ou dans le fichier Web.config. Nous vous déconseillons d'utiliser ces deux approches.

Bien que la solution présente plus de risques que l'utilisation d'une clé de registre limitée, vous pouvez être tenté de stocker la chaîne cryptée dans le fichier Web.config pour faciliter le déploiement. Dans ce cas, utilisez une paire nom-valeur <appSettings>, comme illustré ci-dessous :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <appSettings>  
   <add key="connectionString" value="AQA..bIE=" />
 </appSettings>
 <system.web>
   ...
 </system.web>
</configuration>

Pour accéder au texte crypté à partir de l'élément <appSettings>, utilisez la classe ConfigurationSettings, comme illustré ci-dessous :

using System.Configuration;
private static string GetConnectionString()
{
  return ConfigurationSettings.AppSettings["connectionString"];
}
Ne pas utiliser Persist Security Info='True' ou 'Yes'

Lorsque vous utilisez l'attribut Persist Security Info dans une chaîne de connexion, la propriété ConnectionString dévoile le mot de passe de la chaîne de connexion avant qu'elle ne soit renvoyée à l'utilisateur. La valeur par défaut false (équivalent à l'omission de l'attribut Persist Security Info) détruit les informations une fois la connexion avec la base de données établie.

Sécurisation des fichiers UDL avec des listes de contrôle d'accès restreintes

Si votre application utilise des fichiers de lien de données universel (UDL) avec le fournisseur de données géré ADO.NET pour OLE DB, vous devez utiliser les autorisations NTFS pour limiter l'accès. Utilisez la liste ACL restreinte suivante :

Administrateurs : Contrôle total
Compte du processus : Lire

Remarque : les fichiers UDL ne sont pas cryptés. Une approche plus sûre consiste à crypter la chaîne de connexion à l'aide de DPAPI et de la stocker dans une clé de registre restreinte.

Données sensibles

La plupart des applications Web stockent les données sensibles d'un formulaire ou autre dans la base de données. Si un attaquant parvient à exécuter une requête sur votre base de données, il est impératif que les données sensibles (comme les numéros de cartes de crédit) soient cryptées de manière appropriée.

  • Cryptez les données sensibles si vous devez les stocker.

  • Sécurisez les données sensibles sur le réseau.

  • Stockez les hachages de mot de passe avec une valeur de cryptage aléatoire.

Cryptez les données sensibles si vous devez les stocker

Évitez dans la mesure du possible de stocker les données sensibles. Si vous devez les stocker, cryptez-les.

Cryptage 3DES

Pour stocker dans la base de données les données sensibles, telles que les numéros de cartes de crédit, utilisez un algorithme fiable de cryptage symétrique, comme l'algorithme 3DES.

  • Pour activer le cryptage 3DES pendant le développement

    1. Utilisez la classe RNGCryptoServiceProvider pour générer une clé de cryptage fiable (192 bits, 24 octets).

    2. Sauvegardez la clé de cryptage et stockez la sauvegarde en lieu sûr.

    3. Cryptez la clé à l'aide de DPAIP et stockez-la dans une clé de registre. Utilisez la liste ACL suivante pour sécuriser la clé de registre :

      Administrateurs : Contrôle total
      Compte de processus (ASPNET par exemple) : Lire
  • À l'exécution, pour stocker les données cryptées dans la base de données

    1. Récupérez les données à crypter.

    2. Extrayez la clé de cryptage cryptée du registre.

    3. Utilisez DPAPI pour décrypter la clé de cryptage.

    4. Utilisez la classe TripleDESCryptoServiceProvider avec la clé de cryptage pour crypter les données.

    5. Stockez les données cryptées dans la base de données.

  • À l'exécution, pour décrypter les secrets cryptés

    1. Extrayez les données cryptées de la base de données.

    2. Extrayez la clé de cryptage cryptée du registre.

    3. Utilisez DPAPI pour décrypter la clé de cryptage.

    4. Utilisez la classe TripleDESCryptoServiceProvider pour décrypter les données.

Dans ce processus, si le compte DPAPI utilisé pour crypter la clé de cryptage est endommagé, la sauvegarde de la clé 3DES peut être récupérée et cryptée à l'aide de DPAPI sous un nouveau compte. La nouvelle clé de cryptage peut ensuite être stockée dans le registre et les données de la base peuvent encore être décryptées.

Pour plus de détails sur la création d'une bibliothèque gérée DPAPI, consultez la rubrique « How To: Create a DPAPI Library » de la section « How To » dans l'article « 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.

Sécurisation des données sensibles sur le réseau

Parmi les données sensibles transmises sur le réseau à partir du serveur de base de données ou vers celui-ci peuvent figurer des données spécifiques d'une application ou des identifiants de connexion à la base de données. Pour garantir l'intégrité et la confidentialité des données transmises sur le réseau, utilisez une solution au niveau de la plate-forme (telle que celle fournie par un centre de données sécurisé dans lequel les canaux de communication IPSec sont utilisés entre les serveurs) ou configurez votre application pour qu'elle établisse des connexions SSL avec la base de données. Cette dernière approche nécessite l'installation d'un certificat de serveur sur le serveur de base de données.

Pour plus de détails sur l'utilisation de SSL et d'IPSec, consultez la rubrique « How To: Use IPSec to Provide Secure Communication Between Two Servers » de la section « How To » dans l'article « 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.

Stockage des hachages de mots de passe avec une valeur de cryptage aléatoire

Si vous avez besoin d'implémenter un magasin utilisateur contenant les noms et mots de passe des utilisateurs, ne stockez les mots de passe ni en clair ni en format crypté. Au lieu de stocker les mots de passe, stockez les valeurs hachées non réversibles (nhash) avec une valeur de cryptage aléatoire complémentaire pour réduire les risques d'attaque de dictionnaire.

Remarque : une valeur de cryptage aléatoire « salt » est un nombre aléatoire fiable crypté.

Création d'une valeur de cryptage aléatoire

Le code qui suit montre comment générer une valeur de cryptage aléatoire à l'aide de la fonction de génération de nombres aléatoires fournie par la classe RNGCryptoServiceProvider de l'espace de noms System.Security.Cryptography.

public static string CreateSalt(int size)
{
  RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
  byte[] buff = new byte[size];
  rng.GetBytes(buff);
  return Convert.ToBase64String(buff);
}
Création d'une valeur de hachage (avec valeur de cryptage aléatoire)

L'extrait de code qui suit montre comment générer une valeur de hachage à partir d'un mot de passe fourni et d'une valeur de cryptage aléatoire.

public static string CreatePasswordHash(string pwd, string salt)
{
  string saltAndPwd = string.Concat(pwd, salt);
  string hashedPwd = 
        FormsAuthentication.HashPasswordForStoringInConfigFile(
                                             saltAndPwd, "SHA1");
  return hashedPwd;
}
Informations complémentaires

Pour plus de détails sur l'implémentation d'un magasin utilisateur stockant les hachages de mots de passe avec valeur de cryptage aléatoire, consultez la rubrique « How To: Use Forms Authentication with SQL Server 2000 » de la section « How To » dans l'article « 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..

Gestion des exceptions

Les conditions d'exception peuvent provenir d'erreurs de configuration, de bogues dans votre code ou de l'entrée malveillante de données. Sans gestion appropriée des exceptions, ces situations peuvent révéler des informations sensibles sur l'emplacement et la nature de votre source de données ainsi que de précieux détails de connexion. Les recommandations qui suivent s'appliquent au code d'accès aux données :

  • Bloquez les exceptions ADO.NET et consignez-les dans un journal.

  • Veillez à ce que les connexions de la base de données soient toujours fermées.

  • Utilisez une page générique d'erreur dans vos applications ASP.NET.

Blocage des exceptions ADO.NET et consignation dans un journal

Placez le code d'accès aux données dans un bloc try / catch et gérez les exceptions. Lors de l'écriture du code d'accès aux données ADO.NET, le type d'exception générée par ADO.NET dépend du fournisseur de données. Exemple :

  • Le fournisseur de données .NET Framework SQL Server génère des exceptions de type SqlExceptions.

  • Le fournisseur de données .NET Framework OLE DB génère des exceptions de type OleDbExceptions.

  • Le fournisseur de données .NET Framework ODBC génère des exceptions de type OdbcExceptions.

Blocage des exceptions

Le code qui suit utilise le fournisseur de données .NET Framework SQL Server et vous montre comment bloquer les exceptions de type SqlException.

try
{
  // Code d'accès aux données
}
catch (SqlException sqlex) // plus spécifique
{
}
catch (Exception ex) // moins spécifique
{
}
Consignation des exceptions

Vous devez également consigner les détails de la classe SqlException. Cette classe révèle les propriétés qui contiennent les détails de la condition d'exception. Il s'agit de la propriété Message qui décrit l'erreur, de la propriété Number qui identifie de manière unique le type d'erreur et de la propriété State qui contient des informations complémentaires. La propriété State est généralement utilisée pour indiquer l'occurrence particulière d'une condition d'erreur spécifique. Ainsi, si une procédure stockée génère la même erreur à partir de plusieurs lignes, la propriété State indiquera l'occurrence spécifique. Enfin, une collection Errors contient les objets SqlError qui fournissent des informations détaillées sur l'erreur du serveur SQL.

L'extrait de code qui suit montre comment gérer une condition d'erreur SQL Server avec le fournisseur de données .NET Framework SQL Server :

using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;

// Méthode exposée par un composant de couche d'accès aux données
public string GetProductName( int ProductID )
{
  SqlConnection conn = new SqlConnection(
        "server=(local);Integrated Security=SSPI;database=products");
  // Englobe tout le code d'accès aux données dans un bloc try
  try
  {
    conn.Open();
    SqlCommand cmd = new SqlCommand("LookupProductName", conn );
    cmd.CommandType = CommandType.StoredProcedure;

    cmd.Parameters.Add("@ProductID", ProductID );
    SqlParameter paramPN = 
         cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );
    paramPN.Direction = ParameterDirection.Output;

    cmd.ExecuteNonQuery();
    // Le code final est exécuté avant le renvoi de la méthode
    return paramPN.Value.ToString();  
  }
  catch (SqlException sqlex)
  {
    // Gère la condition d'exception d'accès aux données
    // Consigne les détails spécifiques de l'exception
    LogException(sqlex);
    // Enveloppe l'exception courante dans une exception plus
    // significative et lève une nouvelle exception
    throw new Exception(
                  "Impossible de récupérer les détails de produit pour l'ID de produit : " + 
                   ProductID.ToString(), sqlex );
  }
  finally
  {
    conn.Close(); // Assure la fermeture de la connexion
  }
}

// Routine d'assistance qui consigne les détails de SqlException 
// dans le journal des événements de l'application
private void LogException( SqlException sqlex )
{
  EventLog el = new EventLog();
  el.Source = "CustomAppLog";
  string strMessage;
  strMessage = "L'exception numéro : " + sqlex.Number + 
               "(" + sqlex.Message + ") est survenue.";
  el.WriteEntry( strMessage );

  foreach (SqlError sqle in sqlex.Errors)
  {
    strMessage = "Message : " + sqle.Message +
                 " Numéro : " + sqle.Number +
                 " Procédure : " + sqle.Procedure +
                 " Serveur : " + sqle.Server +
                 " Source : " + sqle.Source +
                 " État : " + sqle.State +
                 " Sévérité : " + sqle.Class +
                 " Numéro de ligne : " + sqle.LineNumber;
    el.WriteEntry( strMessage );
  }
}

Vérification de la fermeture des connexions de la base de données

Si une exception se produit, il est essentiel que les connexions de la base de données soient fermées et que toutes les autres ressources limitées soient libérées. Utilisez des blocs finally ou l'instruction using pour vous assurer que les connexions sont fermées lorsqu'une condition d'exception se produit. Le code ci-dessus illustre l'utilisation du bloc finally. Vous pouvez également utiliser l'instruction using en C#, comme illustré ci-dessous :

using ((SqlConnection conn = new SqlConnection(connString)))
{
  conn.Open();
  // La connexion sera fermée si une exception est générée ou si le 
  // le flux de contrôle quitte l'étendue habituelle de l'instruction using
}

Utilisation d'une page d'erreur générique dans vos applications ASP.NET

Si une application Web ASP.NET ou un service Web appelle votre code d'accès aux données, vous devez configurer l'élément <customErrors> pour éviter que les détails de l'exception ne soient dévoilés à l'utilisateur final. Vous pouvez également spécifier une page d'erreur générique à l'aide de cet élément, comme illustré ci-dessous.

<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />

Définissez les serveurs de production sur mode="On". Utilisez mode="Off" uniquement lorsque vous développez et testez le logiciel avant sa publication. Si vous n'appliquez pas ce principe de précaution, vos utilisateurs finaux recevront des informations détaillées sur l'erreur, comme le montre la figure 14.4. Le nom du serveur de base de données, le nom de la base et les identifiants de connexion peuvent figurer parmi ces informations.

Informations détaillées de l'exception révélant des données sensibles.

Figure 14.4
Informations détaillées de l'exception révélant des données sensibles

La figure 14.4 montre également un certain nombre de vulnérabilités dans le code d'accès aux données, à proximité de la ligne qui a provoqué l'exception. Plus particulièrement :

  • La chaîne de connexion est préprogrammée.

  • Le compte sa sur-privilégié est utilisé pour se connecter à la base de données.

  • Le compte sa a un mot de passe faible.

  • La construction de la commande SQL peut être sujette aux attaques par injection SQL car les valeurs saisies ne sont pas validées et le code n'utilise pas de procédures stockées paramétrées.

Création d'un composant sécurisé d'accès aux données

Le code qui suit présente un exemple d'implémentation de la méthode CheckProductStockLevel utilisée pour interroger une base de données de produits sur la quantité en stock. Le code illustre un certain nombre de caractéristiques de sécurité importantes pour le code d'accès aux données et présentées précédemment dans ce module.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using Microsoft.Win32;
using DataProtection;

public static int CheckProductStockLevel(string productCode)
{
  int quantity = 0;
  // (1) Code protégé par bloc try/catch
  try
  {
    // (2) Entrée validée par une expression régulière
    //     Les messages d'erreur doivent être extraits de l'assembly de ressource pour
    //     faciliter la localisation. Le code de localisation est omis par souci de concision.
    if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
      throw new ArgumentException("Invalid product code" );
    //(3) l'instruction using garantit que la connexion est fermée
    using (SqlConnection conn = new SqlConnection(GetConnectionString()))
    {
      // (4) L'utilisation de procédures stockées paramétrées est une mesure contre
      //    les attaques par injection SQL
      SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
      cmd.CommandType = CommandType.StoredProcedure;

      // Vérification du type des paramètres
      SqlParameter parm = 
               cmd.Parameters.Add("@ProductCode", 
                                  SqlDbType.VarChar,12);
      parm.Value = productCode;
      // Définition du paramètre de sortie
      SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
      retparm.Direction = ParameterDirection.Output;
      conn.Open();
      cmd.ExecuteNonQuery();
      quantity = (int)retparm.Value;
    }
  }
  catch (SqlException sqlex)
  {
    //(5) Les détails complets de l'exception sont consignés. Un message d'erreur générique
    //    est renvoyé à l'appelant, sur la base d'un code d'erreur SQL
    //    Le code d'identification de l'erreur et de la consignation a été omis par souci de clarté
    throw new Exception("Erreur lors du traitement de la requête");
  }
  catch (Exception ex)
  {
    // Consigne les détails complets de l'exception
    throw new Exception("Erreur lors du traitement de la requête");
  }
  return quantity;
}

// (6) La chaîne de connexion à la base cryptée est conservée dans le registre
private static string GetConnectionString()
{
  // Récupération du texte codé dans le registre. Le compte de processus doit
  // obtenir un accès en lecture via la liste de contrôle de la clé
  string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
                                        @"Software\OrderProcessing\")
                                        .GetValue("ConnectionString");
  // Utilisation de la bibliothèque d'assistance DPAPI pour décrypter la chaîne
  DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
  byte[] dataToDecrypt = Convert.FromBase64String(encryptedString);
  return Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null));
}

Le code ci-avant présente les caractéristiques de sécurité suivantes (identifiées par les numéros sur les lignes de commentaires).

  1. Le code d'accès aux données est placé dans un bloc try/catch. Il est essentiel d'empêcher le renvoi d'informations relatives au système à l'appelant en cas d'exception. L'application Web ASP.Net appelante ou le service Web doit traiter l'exception et renvoyer un message d'erreur suffisamment générique au client mais le code d'accès aux données est indépendant.

  2. Les valeurs entrées sont validées à l'aide d'une expression régulière. La référence de produit fournie est vérifiée pour s'assurer qu'elle ne contient que des caractères compris dans les plages A–Z et 0–9 et qu'elle ne dépasse pas 12 caractères. Il s'agit de la première mesure dans un ensemble de contre-mesures conçues pour empêcher les attaques par injection SQL.

  3. L'objet SqlConnection est créé au sein d'une instruction using Microsoft Visual C#®. Cette mesure permet de s'assurer que la connexion est fermée au sein de la méthode qu'une exception se produise ou non. Elle restreint les menaces d'attaques par déni de service qui tentent d'utiliser toutes les connexions à la base de données disponibles. Vous pouvez obtenir une fonctionnalité similaire avec le bloc finally.

  4. Les procédures stockées paramétrées sont utilisées pour accéder aux données. Il s'agit ici d'une autre contre-mesure pour éviter l'injection SQL.

  5. Les informations détaillées sur l'erreur ne sont pas renvoyées au client. Les détails de l'exception sont consignés pour permettre le diagnostic du problème.

  6. La chaîne de connexion cryptée de la base de données est stockée dans le registre. L'un des moyens les plus sûrs pour stocker les chaînes de connexion à la base de données consiste à utiliser DPAPI pour crypter la chaîne et à stocker ensuite le texte de cryptage dans une clé de registre sécurisée et dotée d'une liste de contrôle d'accès restreint. Utilisez par exemple Administrateurs : Contrôle total et un compte de processus Enterprise Services ou ASP.NET : Lecture, selon le processus hébergeant le composant.

    Remarque : le code montre comment extraire la chaîne de connexion du registre, puis la décrypter à l'aide de la bibliothèque d'assistance DPAPI gérée. Vous trouverez cette bibliothèque dans la rubrique « How To: Create a DPAPI Library » de la section « How To » de l'article « Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication ».

Considérations sur la sécurité par code d'accès

Tout accès aux données est soumis à des demandes d'autorisation de sécurité d'accès au code. Votre fournisseur de données gérées ADO.NET détermine les exigences précises. Le tableau qui suit présente les autorisations devant être accordées à vos assemblys d'accès aux données pour chaque fournisseur de données ADO.NET.

Tableau 14.1 Autorisations de sécurité d'accès au code requises par les fournisseurs de données ADO.NET

Fournisseur de données ADO.NET

Autorisation de sécurité d'accès au code requise

SQL Server

SqlClient
L'autorisation prend en charge les appelants de confiance partielle, y compris les applications à approbation moyenne.

OLE DB

OleDbPermission*

Oracle

OraclePermission*

ODBC

OdbcPermission*

*À l'heure où nous écrivons cet article, les fournisseurs OLE DB, Oracle et ODBC ne prennent en charge que les appelants de confiance totale sur les versions 1.0 et 1.1 de .NET Framework. Pour utiliser ces fournisseurs à partir d'applications Web de confiance partielle, vous devez mettre en sandbox le code d'accès aux données qui nécessite un assembly dédié d'accès aux données. Vous trouverez un exemple illustrant la mise en sandbox du code d'accès aux données et l'utilisation d'un fournisseur de données OLE DB à partir d'une application Web de confiance partielle, dans le module 9, « Utilisation de la sécurité d'accès au code avec ASP.NET ».

Si vous utilisez le fournisseur de données SQL Server ADO.NET, votre code doit disposer de l'autorisation SqlClientPermission via la stratégie de sécurité d'accès au code. Les applications Web de confiance partielle et totale disposent de cette autorisation.

Votre code pourra se connecter aux serveurs SQL s'il dispose de l'autorisation SqlClientPermission. Vous pouvez également utiliser cette autorisation pour limiter l'utilisation des chaînes de connexion à la base de données. Vous pouvez ainsi forcer une application à utiliser la sécurité intégrée ou garantir que si la sécurité SQL Server est utilisée, les mots de passe vierges ne seront pas acceptés. La violation des règles que vous spécifiez via SqlClientPermission entraîne une exception de sécurité à l'exécution.

Pour plus d'informations sur l'utilisation de SqlClientPermission pour restreindre l'accès aux données, reportez-vous à la rubrique « Accès aux données » du module 8, « La sécurité d'accès au code en pratique ».

Considérations de déploiement

Un composant d'accès aux données développé et conçu avec les précautions de sécurité reste vulnérable s'il n'est pas déployé de manière sécurisée. Une pratique de déploiement courante consiste à placer le code d'accès aux données et la base de données sur des serveurs distincts. Les serveurs sont souvent séparés par un pare-feu interne, ce qui introduit de nouvelles considérations de déploiement. Développeurs et administrateurs, gardez à l'esprit des problèmes suivants :

  • Limites du pare-feu

  • Gestion de la chaîne de connexion

  • Configuration du compte de connexion

  • Audit de l'ouverture de session

  • Confidentialité et intégrité des données sur le réseau

Limites du pare-feu

Si vous vous connectez à SQL Server via un pare-feu, configurez le pare-feu, le client et le serveur. Vous devez configurer le client à l'aide de l'utilitaire de réseau client SQL Server et le serveur de base de données à l'aide de l'utilitaire de réseau serveur. Par défaut, SQL Server écoute sur le port 1433 mais vous pouvez modifier ce paramètre. Vous devez ensuite ouvrir le port choisi au niveau du pare-feu.

Suivant le mode d'authentification SQL choisi et si votre application utilise des transactions distribuées, il est possible que vous deviez ouvrir plusieurs ports supplémentaires au niveau du pare-feu :

  • Si votre application utilise l'authentification Windows pour se connecter à SQL Server, les ports permettant de prendre en charge l'authentification Kerberos ou NTLM doivent être ouverts.

    Pour les réseaux n'utilisant pas Active Directory, le port TCP 139 est généralement requis pour l'authentification Windows. Pour plus d'informations sur les exigences en matière de port, consultez les articles Technet, « TCP and UDP Port Assignments », à l'adresse http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/tcpip/part4/tcpappc.asp, et « Security Considerations for Administrative Authority », à l'adresse http://www.microsoft.com/technet/security/bestprac/bpent/sec2/seconaa.mspx.

  • Si votre application utilise les transactions distribuées, comme les transactions automatisées COM+, il est possible que vous deviez configurer votre pare-feu pour autoriser le trafic DTC entre les instances DTC distinctes d'une part et entre le DTC et les gestionnaires de ressources tels que SQL Server d'autre part.

Pour obtenir les détails de la configuration, reportez-vous à la section « Ports » dans le module 18, « Sécurisation de votre serveur de base de données ».

Gestion de la chaîne de connexion

De nombreuses applications stockent les chaînes de connexion dans le code essentiellement pour des raisons de performances. Pourtant, les avantages en termes de performances sont négligeables, alors que l'utilisation de la mise en cache du système de fichiers permet, par le stockage des chaînes de connexion dans des fichiers externes, d'obtenir des performances comparables. L'utilisation de fichiers externes pour stocker les chaînes de connexion présente un avantage supérieur en termes d'administration du système.

Pour renforcer la sécurité, l'approche recommandée consiste à utiliser DPAPI pour crypter la chaîne de connexion. Cette méthode est particulièrement importante si votre chaîne de connexion contient des noms d'utilisateur et des mots de passe. Vous devez ensuite décider où stocker la chaîne cryptée. Le registre constitue un emplacement sûr, en particulier si vous utilisez la clé HKEY_CURRENT_USER car son accès est limité aux processus qui s'exécutent sous le compte de l'utilisateur associé. Une alternative pour faciliter le développement consiste à stocker la chaîne cryptée dans le fichier Web.config. Les deux approches ont été traitées dans la section Gestion de la configuration, plus haut dans ce module.

Configuration du compte de connexion

Il est essentiel que votre application utilise un compte le moins privilégié possible pour se connecter à la base de données. C'est là l'une des principales techniques permettant de réduire les menaces d'attaques par injection SQL.

En tant que développeur, vous devez préciser à l'administrateur de la base de données à quelles procédures stockées et tables la connexion de l'application doit avoir accès. Idéalement, vous devriez autoriser la connexion de l'application à n'exécuter qu'un ensemble restreint de procédures stockées, déployées avec l'application.

Utilisez des mots de passe fiables pour le compte Windows ou SQL ou pour les comptes qu'utilise l'application pour se connecter à la base de données.

Consultez la section « Autorisation », plus haut dans ce module pour connaître la stratégie d'autorisation recommandée pour le compte de l'application accédant à la base de données.

Audit de l'ouverture de session

Vous devez configurer SQL Server de manière à consigner les tentatives infructueuses de connexion et éventuellement les tentatives réussies. L'audit des tentatives infructueuses de connexion aide à détecter l'attaquant qui tente de découvrir les mots de passe d'un compte.

Pour plus d'informations sur la façon de configurer l'audit SQL Server, reportez-vous au module 18, « Sécurisation de votre serveur de base de données ».

Confidentialité et intégrité des données sur le réseau

Si vous utilisez l'authentification SQL pour vous connecter à SQL Server, veillez à ce que les informations d'identifications ne soient pas exposées sur le réseau. Installez un certificat sur le serveur de base de données (qui a pour conséquence que SQL Server crypte les informations d'identification) ou utilisez un canal IPSec crypté pour accéder à la base de données.

L'utilisation d'IPSec ou de SSL vers la base de données est recommandé pour protéger les données sensibles de l'application transférées vers la base de données ou à partir de celle-ci. Pour plus d'informations, reportez-vous au module 18, « Sécurisation de votre serveur de base de données ».

Résumé

Ce module a présenté les principales menaces auxquelles est exposé le code d'accès aux données et a mis en évidence les vulnérabilités les plus courantes. L'injection SQL est l'une des principales menaces à connaître. Si vous ne mettez pas en œuvre les contre-mesures présentées dans ce module, un attaquant peut exploiter le code d'accès aux données pour exécuter arbitrairement des commandes dans la base de données. Les mesures de sécurité conventionnelles telles que les pare-feu et SSL n'offrent aucune défense contre les attaques par injection SQL. Vous devez valider consciencieusement les valeurs entrées et utiliser des procédures stockées paramétrées comme système de défense minimal.

Informations complémentaires

Pour plus d'informations, reportez-vous aux ressources suivantes :

  • Retrouvez la liste de contrôle imprimable « Liste de contrôle : sécurisation de l'accès aux données » dans la section « Listes de contrôle » de ce guide.

  • Pour plus d'informations sur la sécurisation de la station de travail du développeur, consultez la rubrique « Procédure : sécurisation de la station de travail du développeur » de la section « Procédures » de ce guide.

  • Pour plus de détails sur l'utilisation de SSL avec SQL Server, consultez la rubrique « How To: Use SSL to Secure Communication with SQL Server 2000 » de la section « How To » dans l'article « 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/SecNetHT19.asp ».

  • Pour plus de détails sur l'utilisation d'IPSec, consultez la rubrique « How To: Use IPSec to Provide Secure Communication Between Two Servers » de la section « How To » dans l'article « 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/SecNetHT18.asp ».

  • Pour plus de détails sur l'utilisation de DPAPI, consultez la rubrique « How To: Create a DPAPI Library » de la section « How To » dans l'article « 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/SecNetHT07.asp ».

Microsoft réalise une enquête en ligne pour recueillir votre opinion sur le site Web de MSDN. Si vous choisissez d’y participer, cette enquête en ligne vous sera présentée lorsque vous quitterez le site Web de MSDN.

Si vous souhaitez y participer,
Afficher:
© 2014 Microsoft