Quand aurais-je besoin d'un SecureString dans .NET?

179

J'essaye de comprendre le but de SecureString de .NET. Depuis MSDN:

Une instance de la classe System.String est à la fois immuable et, lorsqu'elle n'est plus nécessaire, ne peut pas être planifiée par programme pour le garbage collection; autrement dit, l'instance est en lecture seule après sa création et il n'est pas possible de prédire quand l'instance sera supprimée de la mémoire de l'ordinateur. Par conséquent, si un objet String contient des informations sensibles telles qu'un mot de passe, un numéro de carte de crédit ou des données personnelles, il existe un risque que les informations soient révélées après leur utilisation car votre application ne peut pas supprimer les données de la mémoire de l'ordinateur.

Un objet SecureString est similaire à un objet String en ce qu'il a une valeur de texte. Toutefois, la valeur d'un objet SecureString est automatiquement chiffrée, peut être modifiée jusqu'à ce que votre application le marque comme étant en lecture seule et peut être supprimée de la mémoire de l'ordinateur par votre application ou le garbage collector .NET Framework.

La valeur d'une instance de SecureString est automatiquement chiffrée lorsque l'instance est initialisée ou lorsque la valeur est modifiée. Votre application peut rendre l'instance immuable et empêcher toute modification ultérieure en appelant la méthode MakeReadOnly.

Le cryptage automatique est-il le gros avantage?

Et pourquoi ne puis-je pas simplement dire:

SecureString password = new SecureString("password");

au lieu de

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

Quel aspect de SecureString me manque-t-il?

Richard Morgan
la source
3
11 ans plus tard et MS ne recommande plus SecureStringde nouveau développement: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Matt Thomas

Réponses:

4

J'arrêterais d'utiliser SecureString. On dirait que les gars de PG abandonnent leur soutien. Peut-être même le tirer à l'avenir - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring .

Nous devrions supprimer le chiffrement de SecureString sur toutes les plates-formes dans .NET Core - Nous devrions rendre SecureString obsolète - Nous ne devrions probablement pas exposer SecureString dans .NET Core

Joe Healy
la source
1
Le lien est mort mais il semble continuer: docs.microsoft.com/en-us/dotnet/api/… .. avec quelques conseils très faibles sur le chemin vers l'avant execpt "ne pas utiliser les informations d'identification" - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. n'osez pas non plus utiliser un mot de passe pour protéger la clé privée de vos certificats!
felickz
1
Après 11 ans, cette réponse semble maintenant être la «nouvelle» correcte. Les liens semblent devenir obsolètes, mais les conseils de MS sont les suivants: SecureString ne devrait pas être utilisé
Richard Morgan
109

Certaines parties du framework qui utilisent actuellement SecureString:

L'objectif principal est de réduire la surface d'attaque plutôt que de l'éliminer. SecureStringssont «épinglés» dans la RAM afin que le garbage collector ne le déplace pas ou n'en fasse pas de copies. Il s'assure également que le texte brut ne sera pas écrit dans le fichier d'échange ou dans les vidages de mémoire. Le cryptage ressemble plus à une obfuscation et n'arrêtera pas un pirate informatique déterminé, qui serait en mesure de trouver la clé symétrique utilisée pour crypter et décrypter.

Comme d'autres l'ont dit, la raison pour laquelle vous devez créer un SecureStringcaractère par caractère est le premier défaut évident de faire autrement: vous avez probablement déjà la valeur secrète sous forme de chaîne simple, alors à quoi ça sert?

SecureStrings sont la première étape dans la résolution d'un problème Chicken-and-Egg, donc même si la plupart des scénarios actuels nécessitent de les reconvertir en chaînes régulières pour en faire un usage quelconque, leur existence dans le framework signifie désormais un meilleur support pour eux dans le futur - au moins à un point où votre programme ne doit pas être le maillon faible.

Chris Wenham
la source
2
Je l'ai parcouru en regardant la propriété Password de ProcessStartInfo; ne prêtant même pas attention au type, je l'ai juste mis à une chaîne régulière jusqu'à ce que le compilateur m'aboie.
Richard Morgan le
Il ne sera pas facile de trouver la clé de cryptage symétrique, car SecureString est basé sur DPAPI, qui ne stocke pas exactement une clé en texte
brut
1
En outre, ce n'est pas tant le problème de la poule et de l'œuf, car ce n'est pas un remplacement du chiffrement dans le stockage - mais c'est une solution de contournement pour les chaînes .NET gérées et immuables.
AviD le
2
"vous avez probablement déjà la valeur secrète sous forme de chaîne simple, alors à quoi ça sert?" Y a-t-il une réponse à cette question? Il semble que ce soit au mieux une solution «mieux que rien» si vous avez l'intention de conserver un mot de passe en mémoire pendant une période prolongée.
xr280xr
2
Qu'en est-il d'avoir quelques exemples d'utilisation de code simples? Je pense que je comprendrais mieux comment et quand l'utiliser.
codea
37

Edit : n'utilisez pas SecureString

Les directives actuelles indiquent maintenant que la classe ne doit pas être utilisée. Les détails peuvent être trouvés sur ce lien: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Extrait de l'article:

DE0001: SecureString ne doit pas être utilisé

Motivation

  • Le but de SecureStringest d'éviter d'avoir des secrets stockés dans la mémoire du processus sous forme de texte brut.
  • Cependant, même sous Windows, il SecureStringn'existe pas de concept de système d'exploitation.
    • Cela rend simplement la fenêtre qui raccourcit le texte brut; cela ne l'empêche pas totalement car .NET doit encore convertir la chaîne en une représentation en texte brut.
    • L'avantage est que la représentation en texte brut n'est pas une instance de System.String- la durée de vie du tampon natif est plus courte.
  • Le contenu du tableau n'est pas chiffré, sauf sur .NET Framework.
    • Dans .NET Framework, le contenu du tableau de caractères interne est chiffré. .NET ne prend pas en charge le chiffrement dans tous les environnements, que ce soit en raison d'API manquantes ou de problèmes de gestion des clés.

Recommandation

Ne pas utiliser SecureStringpour un nouveau code. Lorsque vous portez du code vers .NET Core, considérez que le contenu du tableau n'est pas chiffré en mémoire.

L'approche générale du traitement des informations d'identification consiste à les éviter et à s'appuyer à la place sur d'autres moyens pour s'authentifier, tels que les certificats ou l'authentification Windows.

Fin de la modification: résumé original ci-dessous

Beaucoup de bonnes réponses; voici un bref résumé de ce qui a été discuté.

Microsoft a implémenté la classe SecureString dans le but de fournir une meilleure sécurité avec les informations sensibles (comme les cartes de crédit, les mots de passe, etc.). Il fournit automatiquement:

  • cryptage (en cas de vidage de mémoire ou de mise en cache de page)
  • épingler en mémoire
  • possibilité de marquer comme lecture seule (pour éviter toute modification ultérieure)
  • construction sûre en ne permettant PAS de passer une chaîne constante

Actuellement, l'utilisation de SecureString est limitée, mais on s'attend à une meilleure adoption à l'avenir.

Sur la base de ces informations, le constructeur de SecureString ne doit pas simplement prendre une chaîne et la découper en tableau de caractères, car la chaîne est épelée contre l'objectif de SecureString.

Information additionnelle:

  • Un article du blog .NET Security parlant à peu près de la même chose que celle décrite ici.
  • Et un autre le revisitant et mentionnant un outil qui PEUT vider le contenu du SecureString.

Edit: J'ai trouvé difficile de choisir la meilleure réponse car il y a de bonnes informations dans beaucoup; dommage qu'il n'y ait pas d'options de réponse assistée.

Richard Morgan
la source
19

Réponse courte

pourquoi ne puis-je pas simplement dire:

SecureString password = new SecureString("password");

Parce que maintenant vous avez passworden mémoire; sans moyen de l'effacer - ce qui est exactement le but de SecureString .

Longue réponse

La raison pour laquelle SecureString existe est que vous ne pouvez pas utiliser ZeroMemory pour effacer les données sensibles lorsque vous en avez terminé. Il existe pour résoudre un problème qui existe à cause du CLR.

Dans une application native standard , vous appelleriez SecureZeroMemory:

Remplit un bloc de mémoire avec des zéros.

Remarque : SecureZeroMemory est identique à ZeroMemory, sauf que le compilateur ne l'optimisera pas.

Le problème est que vous ne pouvez pas appeler ZeroMemoryou à l' SecureZeroMemoryintérieur de .NET. Et dans .NET, les chaînes sont immuables; vous ne pouvez même pas écraser le contenu de la chaîne comme vous pouvez le faire dans d'autres langues:

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

Alors que peux-tu faire? Comment pouvons-nous offrir la possibilité dans .NET d'effacer un mot de passe ou un numéro de carte de crédit de la mémoire lorsque nous en avons terminé?

La seule façon de le faire serait de placer la chaîne dans un bloc de mémoire natif , où vous pouvez ensuite appeler ZeroMemory. Un objet mémoire natif tel que:

  • un BSTR
  • un HGLOBAL
  • Mémoire non gérée CoTaskMem

SecureString redonne la capacité perdue

Dans .NET, les chaînes ne peuvent pas être effacées lorsque vous en avez terminé avec elles:

  • ils sont immuables; vous ne pouvez pas écraser leur contenu
  • tu ne peux pas Disposed'eux
  • leur nettoyage est à la merci du ramasse-miettes

SecureString existe comme un moyen de transmettre la sécurité des chaînes et de garantir leur nettoyage lorsque vous en avez besoin.

Vous avez posé la question:

pourquoi ne puis-je pas simplement dire:

SecureString password = new SecureString("password");

Parce que maintenant vous avez passworden mémoire; sans moyen de l'essuyer. Il est bloqué là jusqu'à ce que le CLR décide de réutiliser cette mémoire. Vous nous avez remis là où nous avons commencé; une application en cours d'exécution avec un mot de passe dont nous ne pouvons pas nous débarrasser et où un vidage de la mémoire (ou Process Monitor) peut voir le mot de passe.

SecureString utilise l'API de protection des données pour stocker la chaîne chiffrée en mémoire; de cette façon, la chaîne n'existera pas dans les fichiers d'échange, les vidages sur incident ou même dans la fenêtre des variables locales avec un collègue qui regarde votre devrait.

Comment lire le mot de passe?

Puis se pose la question: comment interagir avec la chaîne? Vous ne voulez absolument pas d'une méthode comme:

String connectionString = secureConnectionString.ToString()

car maintenant vous êtes de retour là où vous avez commencé - un mot de passe dont vous ne pouvez pas vous débarrasser. Vous voulez forcer les développeurs à gérer correctement la chaîne sensible - afin qu'elle puisse être effacée de la mémoire.

C'est pourquoi .NET fournit trois fonctions d'assistance pratiques pour rassembler un SecureString dans une mémoire non managée:

Vous convertissez la chaîne en un objet blob de mémoire non géré, vous la gérez et l'effacez à nouveau.

Certaines API acceptent SecureStrings . Par exemple dans ADO.net 4.5, SqlConnection.Credential prend un ensemble SqlCredential :

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

Vous pouvez également modifier le mot de passe dans une chaîne de connexion:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

Et il y a beaucoup d'endroits dans .NET où ils continuent d'accepter une chaîne simple à des fins de compatibilité, puis se retournent rapidement et la placent dans un SecureString.

Comment mettre du texte dans SecureString?

Cela laisse toujours le problème:

Comment puis-je obtenir un mot de passe dans SecureString en premier lieu?

C'est le défi, mais le but est de vous faire réfléchir à la sécurité.

Parfois, la fonctionnalité est déjà fournie pour vous. Par exemple, le contrôle WPF PasswordBox peut vous renvoyer directement le mot de passe saisi sous forme de SecureString :

PasswordBox.SecurePassword, propriété

Obtient le mot de passe actuellement détenu par PasswordBox en tant que SecureString .

Ceci est utile car partout où vous aviez l'habitude de transmettre une chaîne brute, vous avez maintenant le système de types se plaignant que SecureString est incompatible avec String. Vous voulez aller le plus longtemps possible avant de devoir reconvertir votre SecureString en chaîne régulière.

La conversion d'un SecureString est assez simple:

  • SecureStringToBSTR
  • PtrToStringBSTR

un péché:

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

Ils ne veulent vraiment pas que vous le fassiez.

Mais comment obtenir une chaîne dans un SecureString? Eh bien, ce que vous devez faire est d'arrêter d'avoir un mot de passe dans une chaîne en premier lieu. Vous aviez besoin de l'avoir dans autre chose . Même un Char[]tableau serait utile.

C'est à ce moment que vous pouvez ajouter chaque caractère et effacer le texte brut lorsque vous avez terminé:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

Vous avez besoin de votre mot de passe stocké dans une mémoire que vous pouvez effacer. Chargez-le dans SecureString à partir de là.


tl; dr: SecureString existe pour fournir l'équivalent de ZeroMemory .

Certaines personnes ne voient pas l'intérêt d' effacer le mot de passe de l'utilisateur de la mémoire lorsqu'un appareil est verrouillé , ou d' effacer les frappes de la mémoire après leur authentification . Ces personnes n'utilisent pas SecureString.

Ian Boyd
la source
14

Il existe très peu de scénarios dans lesquels vous pouvez raisonnablement utiliser SecureString dans la version actuelle du Framework. Ce n'est vraiment utile que pour interagir avec des API non gérées - vous pouvez le rassembler à l'aide de Marshal.SecureStringToGlobalAllocUnicode.

Dès que vous le convertissez vers / depuis un System.String, vous avez atteint son objectif.

L' exemple MSDN génère le SecureString un caractère à la fois à partir de l'entrée de la console et transmet la chaîne sécurisée à une API non gérée. C'est plutôt alambiqué et irréaliste.

Vous pouvez vous attendre à ce que les futures versions de .NET prennent davantage en charge SecureString, ce qui le rendra plus utile, par exemple:

  • SecureString Console.ReadLineSecure () ou similaire pour lire l'entrée de la console dans un SecureString sans tout le code alambiqué de l'exemple.

  • Remplacement de WinForms TextBox qui stocke sa propriété TextBox.Text en tant que chaîne sécurisée afin que les mots de passe puissent être saisis en toute sécurité.

  • Extensions aux API liées à la sécurité pour permettre aux mots de passe d'être transmis en tant que SecureString.

Sans ce qui précède, SecureString aura une valeur limitée.

Joe
la source
12

Je crois que la raison pour laquelle vous devez ajouter des caractères au lieu d'une instanciation plate est que, en arrière-plan, le fait de passer "mot de passe" au constructeur de SecureString place cette chaîne de "mot de passe" en mémoire, ce qui va à l'encontre du but de la chaîne sécurisée.

En ajoutant, vous ne mettez en mémoire qu'un caractère à la fois, ce qui n'est probablement pas adjacent l'un à l'autre, ce qui rend la reconstruction de la chaîne d'origine beaucoup plus difficile. Je peux me tromper ici mais c'est ainsi que cela m'a été expliqué.

Le but de la classe est d'empêcher les données sécurisées d'être exposées via un vidage de mémoire ou un outil similaire.

JoshReedSchramm
la source
11

MS a constaté que sur certaines instances de plantage du serveur (bureau, peu importe), il y avait des moments où l'environnement d'exécution faisait un vidage de la mémoire exposant le contenu de ce qui est en mémoire. Secure String le chiffre en mémoire pour empêcher l'attaquant de récupérer le contenu de la chaîne.

kemiller2002
la source
5

L'un des grands avantages d'un SecureString est qu'il est censé éviter la possibilité que vos données soient stockées sur le disque en raison de la mise en cache de la page. Si vous avez un mot de passe en mémoire et que vous chargez ensuite un programme ou un ensemble de données volumineux, votre mot de passe peut être écrit dans le fichier d'échange lorsque votre programme est paginé à court de mémoire. Avec un SecureString, au moins les données ne resteront pas indéfiniment sur votre disque en texte clair.

Jason Z
la source
4

Je suppose que c'est parce que la chaîne est censée être sécurisée, c'est-à-dire qu'un hacker ne devrait pas être capable de la lire. Si vous l'initialisez avec une chaîne, le pirate pourrait lire la chaîne d'origine.

OregonGhost
la source
4

Eh bien, comme l'indique la description, la valeur est stockée cryptée, ce qui signifie qu'un vidage de la mémoire de votre processus ne révélera pas la valeur de la chaîne (sans un travail assez sérieux).

La raison pour laquelle vous ne pouvez pas simplement construire un SecureString à partir d'une chaîne constante est que vous auriez alors une version non chiffrée de la chaîne en mémoire. Le fait de vous limiter à créer la chaîne en morceaux réduit le risque d'avoir la chaîne entière en mémoire à la fois.

Mark Bessey
la source
2
S'ils limitent la construction à partir d'une chaîne constante, alors la ligne foreach (char c dans "password" .ToCharArray ()) vaincrait cela, non? Cela devrait être pass.AppendChar ('p'); pass.AppendChar ('a'); etc?
Richard Morgan le
Oui, vous pouvez facilement jeter le peu de protection que SecureString vous offre. Ils essaient de vous empêcher de vous tirer une balle dans le pied. De toute évidence, il doit y avoir un moyen d'obtenir la valeur dans et hors d'un SecureString, sinon vous ne pouvez pas l'utiliser pour quoi que ce soit.
Mark Bessey le
1

Un autre cas d'utilisation est lorsque vous travaillez avec des applications de paiement (POS) et que vous ne pouvez tout simplement pas utiliser des structures de données immuables pour stocker des données sensibles car vous êtes un développeur prudent. Par exemple: si je stocke des données de carte sensibles ou des métadonnées d'autorisation dans une chaîne immuable, il y aura toujours le cas où ces données seront disponibles en mémoire pendant une période de temps significative après leur suppression. Je ne peux pas simplement l'écraser. Un autre avantage énorme où ces données sensibles sont conservées en mémoire cryptées.

romain
la source