Framework .Net 2.0 : la classe SecureStringDate de publication : 27/02/2006
Par
Ronald VASSEUR (autres articles)

La classe SecureString est une nouveauté du Framework .Net 2.0, elle a
entre autre l'avantage d'automatiquement crypter sa valeur, elle s'avère
très utile dans de nombreuses situations, et comble à merveille les
limitations des System.String au niveau de la sécurité.
Introduction
1. Les faiblesses de System.String
2. Présentation de Security.SecureString
2.1. Différences et nouveautés
2.2. Membres de la classe SecureString
3. Utilisation de la classe SecureString
3.1. Principales actions sur les SecureString
3.2. Cryptage de la chaîne de caractères
3.3. Décryptage de la chaîne de caractères
4. Mini-application d'exemple
4.1. Application Windows-Form
Ressources
Conclusion
Introduction
En matière de sécurité, la version 2.0 du Framework apporte de
nombreuses nouveautés, l'une d'entre elle est la classe SecureString.
Cette classe est similaire à la classe System.String, à certaines
différences, dont les principales sont que la valeur d'une
SecureString est automatiquement cryptée, et qu'une SecureString
est mutable, c'est-à-dire 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 avec une String classique, mais
n'entrons pas plus dans les détails maintenant, nous verrons
cela par la suite.
1. 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 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, c'est-à-dire que l'ancienne et la nouvelle version cohabitent dans la mémoire.
- Elles ne sont pas fixes en mémoire, c'est-à-dire que le Garbage Collector peut les déplacer en mémoire, et en créer plusieurs copies en mémoire.
Comme vous pouvez le constater, tous 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 passes, des chaînes de connexion,
ou des numéros de cartes bancaires par exemple. Voyons sans plus
tarder en quoi se différencient les SecureString.
2. 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é.
2.1. 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 sur le plan de la sécurité, vont changer la donne. Voici
sans attendre les principales différences :
- La valeur d'un objet SecureString est automatiquement crypté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(), qui rend toute modification impossible.
- A 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
au niveau 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 à 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 bancaires.
 |
Remarque : pour le cryptage et le décryptage des données,
SecureString s'appuie sur la Data Protection API (ou DPAPI)
intégrée à Windows.
|
2.2. 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 |
Insert 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. |
3. Utilisation de la classe SecureString
Dans cette section, vous allez voir quelles sont les principales
fonctionnalités de l'objet SecureString, ainsi que comment les
mettre en application.
3.1. 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 simplement
d'utiliser la méthode MakeReadOnly(), après cela il n'est plus
possible de modifier ce marquage Read-Only, la valeur est
devenue immutable. En cas de tentative de modification une
InvalideOperationException est levée. Voici un code exemple.
| SecureString en lecture seule | 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 au cas où la
valeur de l'instance d'origine est Read-Only(), 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 ajouter en fin de
chaîne et automatiquement cryptés, par l'intermédiaire de
la DPAPI dont nous avons parlé un peu plus tôt.
| Ajouter des caractères | Dim maSecureString as New SecureString
maSecureString.AppendChar("e") |
Effacer le contenu d'une SecureString :
La méthode Clear() nous permet d'effacer en mémoire la valeur
de l'instance de SecureString concernée. Cette méthode efface
physiquement cette valeur dans la mémoire, il s'agît 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 crypté, les
"bonnes pratiques" recommande 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, comme 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.
3.2. Cryptage de la chaîne de caractères
Le cryptage de la valeur d'une SecureString est automatique.
Dès que des caractères sont placés dans une
SecureString, ils sont alors immédiatement cryptés. Cela signifie
que pour crypter une chaîne de caractères dans une SecureString
il suffit donc de faire ceci :
| Cryptage d'une SecureString | 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 crypté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.
3.3. Décryptage de la chaîne de caractères
Pour décrypter une SecureString, vous allez voir que les
choses ne sont pas aussi évidentes que l'on pourrait le croire,
cependant rien d'insurmontable. La classe SecureString ne
possède pas une méthode qui permette de décrypter 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 cryptage
automatique des chaînes, est de pouvoir l'effacer de la mémoire,
elle-même, ou son contenu, avec certitude, sans pour autant
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écryptage, pour la simple et bonne raison que
si l'on devait placer la valeur décryptée dans un objet de type
System.String se trouvant lui-même dans un emplacement mémoire
managée et sous contrôle du Garbage Collector, nous perdrions
alors un pan fondamental du secret de la valeur crypté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écrypté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 au 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 :
| Décryptage d'une SecureString |
Dim monPointeur As IntPtr = Marshal.SecureStringToBSTR(maSecrureString)
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étails :
- 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écrypté 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 jeu d'enfants dans la réalité du développement.
Un exemple valant souvent autant, si ce n'est mieux, que de longs discours, alors
voyons sans plus tarder une petite application que j'ai réalisé
pour illustrer mes propos.
4. Mini-application d'exemple
4.1. 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 suivante démontre ces dires :
 La valeur n'est pas visible
 Décryptage de la valeur de la SecureString.
 La valeur de la SystemString apparaît en clair :( ...
SecureString est utilisable pour les applications Windows-Forms
mais évidemment aussi pour les applications Web-Forms et console,
n'oubliez pas 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'étendrais pas
plus sur cette application, et les commentaires sont
je pense suffisants.
| Mini-application |
Option Explicit On
Option Strict On
Imports System.Security
Imports System.Runtime.InteropServices
Public Class Form1
#Region "System.Security.SecureString"
Private maSecrureString As New SecureString
Private Sub tbSecureString_KeyPress(ByVal sender As Object, ByVal e As _
System.Windows.Forms.KeyPressEventArgs) _
Handles tbSecureString.KeyPress
maSecrureString.AppendChar(e.KeyChar)
e.KeyChar = Char.Parse("*")
End Sub
Private Sub btSecureString_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btSecureString.Click
Dim monPointeur As IntPtr = Marshal.SecureStringToBSTR(maSecrureString)
If Marshal.PtrToStringUni(monPointeur) = "developpez" Then
maSecrureString.Clear()
Me.tbSecureString.Text = Nothing
MessageBox.Show("Mot de passe valide")
Else
maSecrureString.Clear()
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
If Me.tbSystemString.Text = "motdepasse" Then
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
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, quand à elle,
crypte automatiquement son contenu, plus exactement elle le crypte caractère à caractère.
Les autres points forts de cette classe sont nombreux, j'espère vous les
avoir montré, 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.
 
|