Introduction▲
En matière de sécurité, la version 2.0 du Framework apporte de nombreuses nouveautés. L’une d'entre elles est la classe SecureString. Cette classe est similaire à la classe System.String, à certaines différences près, dont les principales sont que la valeur d'une SecureString est automatiquement chiffrée, et qu’elle est mutable. Cela signifie que lorsque l'on modifie sa valeur, elle est réellement modifiée en mémoire, et non simplement écrite dans un nouvel emplacement comme une String classique. Mais n'entrons pas plus dans les détails maintenant, nous verrons cela par la suite.
I. Les faiblesses de System.String▲
Pour comprendre les apports et l'utilité des SecureString, je pense qu'il faut d'abord voir les défauts et les manques des System.String. En voici une liste non exhaustive :
- impossible de les effacer de la mémoire, du moins au moment où on le souhaite et avec certitude ;
- ces chaînes de caractères restent en clair dans la mémoire, donc sont potentiellement peu sûres ;
- elles sont non mutables, c'est-à-dire qu'en cas de modification de la valeur, celle-ci n'est pas écrasée en mémoire par la nouvelle valeur, mais écrite à un autre emplacement en mémoire. L'ancienne et la nouvelle version cohabitent donc dans la mémoire ;
- elles ne sont pas fixes en mémoire, c'est-à-dire que le Garbage Collector peut les déplacer et en créer plusieurs copies en mémoire.
Comme vous pouvez le constater, tout cela fait de la System.String un candidat potentiellement « dangereux » (ne tombons quand même pas dans la paranoïa…), surtout lorsque l'on est appelé à employer des données sensibles comme des mots de passe, des chaînes de connexion ou des numéros de carte bancaire par exemple. Voyons sans plus tarder en quoi se différencient les SecureString.
II. Présentation de Security.SecureString▲
Tout d'abord, précisons que la classe SecureString appartient au namespace Security, qui comme son nom l'indique contient un ensemble de classes et d'interfaces pour la gestion de la sécurité.
II-A. Différences et nouveautés▲
Un objet instancié à partir de la classe SecureString se présente en quelque sorte comme un objet System.String ordinaire. Il contient donc une valeur texte, mais il y a des différences qui vont changer la donne sur le plan de la sécurité. Voici sans attendre les principales différences :
- la valeur d'un objet SecureString est automatiquement chiffrée ;
- la valeur peut être modifiée, c'est-à-dire qu'en cas de modification de la valeur, celle-ci est écrasée en mémoire, et non écrite dans un nouvel emplacement comme avec un objet System.String qui souvent fait cohabiter ancienne et nouvelle valeur ;
- il est possible d'empêcher la modification de la valeur d'une SecureString grâce à la méthode MakeReadOnly() ;
- à la différence d'un objet System.String, un objet SecureString peut être effacé de la mémoire depuis le code de votre application ou par le Garbage Collector ;
- autre différence notable, la classe SecureString ne possède aucune méthode permettant la comparaison ou la modification de sa valeur, un objet SecureString est donc de ce point de vue mieux protégé.
Comme on le voit, les différences majeures entre un objet SecureString et String sont toutes relatives à la sécurité. Grâce à SecureString, nous avons enfin une solution aux faiblesses de la sécurité de System.String. Dans ces conditions la valeur réelle d'un objet SecureString est beaucoup plus difficile d'accès pour une personne tentant d'obtenir des données de manière frauduleuse. Certes cela n'est pas impossible, mais qu'est-ce qui l'est réellement en informatique ?
Au travers de cela nous pouvons dire que la version 2.0 du Framework .Net nous apporte, par le biais de l'objet SecureString, le candidat idéal pour traiter et utiliser des données sensibles comme peuvent l'être par exemple des mots de passe ou encore des numéros de carte bancaire.
Remarque : pour le chiffrage et le déchiffrage des données, SecureString s'appuie sur la Data Protection API (ou DPAPI) intégrée à Windows.
II-B. Membres de la classe SecureString▲
Voyons maintenant comment utiliser concrètement cette nouvelle classe du namespace Security.
Méthode |
Description |
---|---|
AppendChar |
Ajoute un caractère à la fin de la SecureString courante. |
Clear |
Efface la valeur de la SecureString courante. |
Copy |
Copie l'instance de la SecureString courante. |
Dispose |
Libère toutes les ressources utilisées par la SecureString courante. |
Equals |
Détermine si deux instances d'objet sont égales. |
GetType |
Récupère le type de l'instance de l'objet courant. |
InsertAt |
Insère un caractère dans la SecureString à l'index spécifié. |
IsReadOnly |
Booléen qui détermine si la valeur de la SecureString est en lecture seule. |
MakeReadOnly |
Met la valeur de SecureString en lecture seule. |
ReferenceEquals |
Détermine si les instances d'objet spécifiées sont les mêmes. |
RemoveAt |
Supprime un caractère dans la SecureString à l'index spécifié. |
SetAt |
Remplace un caractère dans la SecureString à l'index spécifié. |
ToString |
Retourne une chaîne qui représente l'objet courant. |
III. Utilisation de la classe SecureString▲
Dans cette section, vous allez voir quelles sont les principales fonctionnalités de l'objet SecureString et comment les mettre en application.
III-A. Principales actions sur les SecureString▲
Mise en lecture seule
Il est possible de faire en sorte qu'une SecureString ne soit accessible qu'en lecture seule. Pour cela il suffit d'utiliser la méthode MakeReadOnly(). Après cela il n'est plus possible de modifier ce marquage Read-Only, la valeur est devenue immuable. En cas de tentative de modification, une InvalideOperationException est levée. Voici un code exemple.
Dim
maSecureString as
New
SecureString
maSecureString.MakeReadOnly
(
)
Détecter si une SecureString est Read-Only
Pour détecter si une SecureString est Read-Only, il suffit d'utiliser la méthode IsReadOnly() qui renvoie un booléen (True ou False) en fonction bien évidemment de l'état du Read-Only de l'instance concernée.
Récupérer la longueur d'une SecureString
Il existe la propriété Length qui permet de récupérer la longueur de la SecureString courante. Elle représente donc le nombre de caractères qui composent la valeur de cet objet.
Copier une instance de SecureString
La méthode Copy() permet de copier une instance de SecureString déjà en mémoire. Il faut cependant faire attention si la valeur de l'instance d'origine est Read-Only(), car sa copie, elle, ne le sera pas.
Ajout de caractères à une SecureString
La méthode AppendChar() permet d'ajouter des caractères à une SecureString. Ces caractères seront alors ajoutés en fin de chaîne et automatiquement chiffrés par l'intermédiaire de la DPAPI dont nous avons parlé un peu plus tôt.
Dim
maSecureString as
New
SecureString
maSecureString.AppendChar
(
"e"
)
Effacer le contenu d'une SecureString
La méthode Clear() nous permet d'effacer de la mémoire la valeur de l'instance de SecureString concernée. Cette méthode efface physiquement cette valeur dans la mémoire, il s'agit donc d'une méthode très sûre, et qui doit être utilisée aussi souvent que possible pour éviter de laisser le contenu d'une SecureString en mémoire. Même si celui-ci est chiffré, les « bonnes pratiques » recommandent de l'effacer.
Supprimer une instance de SecureString
La classe SecureString implémente l'interface IDisposable, il est donc possible en appelant la méthode Dispose() de libérer les ressources utilisées, notamment en supprimant totalement de la mémoire l'objet SecureString. Cette méthode écrit des zéros binaires sur toute la zone mémoire contenant la valeur de cet objet.
D'autres méthodes sont intéressantes, par exemple InsertAt() et RemoveAt() qui permettent respectivement d'ajouter et de supprimer un caractère à un index donné dans une SecureString ; je vous renvoie à la documentation pour ces méthodes qui sont très simples d'usage.
III-B. Chiffrage de la chaîne de caractères▲
Le chiffrage de la valeur d'une SecureString est automatique. Dès que des caractères sont placés dans une SecureString, ils sont immédiatement chiffrés. Cela signifie que pour chiffrer une chaîne de caractères dans une SecureString, il suffit de faire ceci :
Dim
maSecureString As
New
SecureString
For
Each
unChar As
Char
In
"ma chaîne à placer dans une SecureString"
maSecureString.AppendChar
(
unChar)
Next
Vous voyez qu'il faut utiliser la méthode AppendChar() pour ajouter un à un les caractères constituant la chaîne. Ils sont alors chiffrés au fur et à mesure de leur placement dans la SecureString. Il n'est pas possible d'assigner directement une chaîne de caractères complète à une SecureString.
III-C. Déchiffrage de la chaîne de caractères▲
Pour déchiffrer une SecureString, vous allez voir que les choses ne sont pas aussi évidentes que l'on pourrait le croire. Rien d'insurmontable cependant. La classe SecureString ne possède pas de méthode qui permette de déchiffrer la chaîne, et cela pour une raison évidente de sécurité que je vais essayer de vous expliquer.
Un des principaux avantages des SecureString, outre le chiffrage automatique des chaînes, est de pouvoir l'effacer de la mémoire, elle-même ou son contenu, avec certitude, sans avoir à attendre que cela soit effectué par le Garbage Collector. De plus, nous savons aussi que celui-ci ne pourra pas copier plusieurs fois la valeur de la SecureString à des endroits différents de la mémoire, ou même faire cohabiter des « versions » successives de la valeur prise par cette SecureString, ce qui, je vous le rappelle, peut être le cas avec les System.String.
Tout cela pour vous dire que la classe SecureString ne possède pas de méthode déchiffrage, pour la simple et bonne raison que si l'on devait placer la valeur déchiffrée dans un objet de type System.String se trouvant lui-même dans un emplacement mémoire managé et sous contrôle du Garbage Collector, nous perdrions alors un pan fondamental du secret de la valeur chiffrée. Pour éviter ces « désagréments », nous allons donc utiliser la classe Marshal du namespace System.Runtime.InteropServices qui va nous permettre d'allouer un emplacement mémoire non managé pour y placer la valeur déchiffrée de notre SecureString. Ainsi, nous pourrons remettre à zéro cet emplacement mémoire après l'avoir utilisé, ce qui est très important sur le plan de la sécurité. Bien entendu, je simplifie au maximum tous ces processus dans mon explication pour rester clair et compréhensible par le plus grand nombre. Pour plus de détails, regardez le code qui suit, et qui vous montre comment procéder :
' Récupération du pointeur
Dim
monPointeur As
IntPtr =
Marshal.SecureStringToBSTR
(
maSecrureString)
' Vérification de la validité du mot de passe
If
Marshal.PtrToStringUni
(
monPointeur) =
"developpez"
Then
...
Vous voyez donc que l'on utilise principalement deux méthodes de la classe Marshal : SecureStringToBSTR et PtrToStringUni. Voyons leur utilité un peu plus en détail :
- SecureStringToBSTR : cette méthode permet d'allouer une BSTR (ou Basic String) dans un espace mémoire non managé, et d'y placer le contenu déchiffré d'une SecrureString ;
- PtrToStringUni : cette méthode permet d'allouer la mémoire non managée pour une SystemString managée et y place le nombre de caractères spécifiés venant d'une chaîne de caractères Unicode non managée.
Même si le principe ne vous paraît pas évident à première vue, ceci devient un jeu d'enfants dans la réalité du développement. Un exemple valant souvent autant, si ce n'est mieux, que de longs discours, voyons sans plus tarder une petite application que j'ai réalisée pour illustrer mes propos.
IV. Miniapplication d'exemple▲
IV-A. Application Windows-Form▲
Cette application utilise les classes System.String et System.Security.SecureString. Au-delà du code exemple qu'elle vous fournit, il faut aussi regarder de plus près son comportement en mode « débug ». En plaçant un point d'arrêt sur le test de validité du mot de passe dans le cas de l'utilisation d'une SecureString, puis en procédant en mode « pas à pas », on peut voir ce que contient réellement la TextBox (ici des étoiles), ou encore voir que le contenu de la SecureString n'est pas disponible, seule la longueur de la chaîne est affichée. Ici, à aucun moment, on ne voit apparaître le contenu de la SecureString, ce qui est totalement différent dans le cas d'une System.String où l'on peut voir son contenu en clair. Les quelques captures d'écran suivantes démontrent ces dires :
SecureString est utilisable pour les applications Windows-Forms, mais évidemment aussi pour les applications Web-Forms et console, n'oubliez pas que nous utilisons .Net :).
Le code source complet de cette application est disponible ci-dessous, mais aussi si nécessaire en téléchargement sous la forme d'une solution Visual Studio 2005 (*.sln) dans la section « Ressources » de cet article. Je ne m'étendrai pas plus sur cette application, et les commentaires sont, je pense suffisants.
'**************************************************************
'
' Mini application montrant l'utilisation de la nouvelle classe
' SecureString du Framework .Net 2.0
' Ce code est libre de droits, vous pouvez donc l'utiliser comme
' bon vous semble. Ce code est donné à titre d'exemple, et par
' conséquent n'offre aucune garantie, de quelque nature que ce
' soit.
'
'**************************************************************
Option
Explicit
On
Option
Strict
On
Imports
System.Security
Imports
System.Runtime.InteropServices
Public
Class
Form1
#Region
"System.Security.SecureString"
' Instanciation d'un objet SecureString
Private
maSecrureString As
New
SecureString
' Code permettant de ne pas afficher le mot de passe
' et qui remplace les caractères par des étoiles
Private
Sub
tbSecureString_KeyPress
(
ByVal
sender As
Object
, ByVal
e As
_
System.Windows.Forms.KeyPressEventArgs
) _
Handles
tbSecureString.KeyPress
' Attention : ce code ne fonctionne que si le mot de passe est saisi en une seule
' et unique fois. Si l'utilisateur l'efface ou le modifie de nouveau après la saisie
' les modifications ne sont pas répercutées en mémoire. Il faudra donc prévoir et gérer
' ces situations si cela est nécessaire, sinon le mot de passe sera validé uniquement
' s'il a été saisi "d'un seul trait".
' Ajoute chaque nouveau caractère à la SecureString
maSecrureString.AppendChar
(
e.KeyChar
)
' Met une étoile dans la TextBox pour remplacer le caractère intercepté
e.KeyChar
=
Char
.Parse
(
"*"
)
End
Sub
Private
Sub
btSecureString_Click
(
ByVal
sender As
System.Object
, ByVal
e As
_
System.EventArgs
) Handles
btSecureString.Click
' Récupération du pointeur
Dim
monPointeur As
IntPtr =
Marshal.SecureStringToBSTR
(
maSecrureString)
' Vérification de la validité du mot de passe
If
Marshal.PtrToStringUni
(
monPointeur) =
"developpez"
Then
' Efface en mémoire le contenu chiffré de la SecureString
maSecrureString.Clear
(
)
' Efface les étoiles qui sont dans la TextBox
Me
.tbSecureString.Text
=
Nothing
MessageBox.Show
(
"Mot de passe valide"
)
Else
' Efface en mémoire le contenu chiffré de la SecureString
maSecrureString.Clear
(
)
' Efface les étoiles qui sont dans la TextBox
Me
.tbSecureString.Text
=
Nothing
MessageBox.Show
(
"Mot de passe non valide"
)
End
If
End
Sub
#End
Region
#Region
"System.String"
Private
Sub
btSystemString_Click
(
ByVal
sender As
System.Object
, ByVal
e As
_
System.EventArgs
) Handles
btSystemString.Click
' Vérification de la validité du mot de passe
If
Me
.tbSystemString.Text
=
"motdepasse"
Then
' Efface les étoiles qui sont dans la TextBox
Me
.tbSystemString.Text
=
Nothing
MessageBox.Show
(
"Mot de passe valide"
)
Else
MessageBox.Show
(
"Mot de passe non valide"
)
End
If
End
Sub
#End
Region
End
Class
Ressources▲
- Projet Visual Studio 2005 (VB.Net) Winforms (97 Ko) : Cliquez ici
- Le namespace System.Runtime.InteropServices : Cliquez ici
- Le namespace System.Security : Cliquez ici
- La classe SecureString : Cliquez ici
- La classe Marshal : Cliquez ici
Conclusion▲
Comme vous l'avez vu au cours de cet article, la classe SecureString permet de combler une lacune de la classe String standard. En effet, celle-ci stocke sa valeur en clair dans la mémoire. SecureString, quant à elle, chiffre automatiquement son contenu, plus exactement elle le chiffre caractère à caractère. Les autres points forts de cette classe sont nombreux, j'espère vous les avoir montrés, et surtout que la prochaine fois vous aurez le réflexe d'utiliser cet objet, qui, il est vrai, n'est malheureusement pas encore très connu, mais maintenant vous n'avez plus d'excuse !
Un très grand merci à Freegreg pour la relecture de cet article.