Framework .Net 2.0 : la classe SecureString

Ronald VASSEUR

La classe SecureString est une nouveauté du Framework .Net 2.0. Elle a entre autres l'avantage d'automatiquement chiffrer 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é.

- Titre : Framework .Net 2.0 : la classe SecureString

- Auteur : Ronald VASSEUR

- Parution : 13 février 2006

- Mise à jour : 28 mars 2020

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2006 Ronald Vasseur. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Introduction

- I. Les faiblesses de System.String

- II. Présentation de Security.SecureString

- II-A. Différences et nouveautés

- II-B. Membres de la classe SecureString

- III. Utilisation de la classe SecureString

- III-A. Principales actions sur les SecureString

- III-B. Chiffrage de la chaîne de caractères

- III-C. Déchiffrage de la chaîne de caractères

- IV. Miniapplication d'exemple

- IV-A. 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 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.

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 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.

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 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 :

Chiffrage 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 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 :

Déchiffrage d'une SecureString

' 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 :

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 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

'**************************************************************
'
' 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.