Come salvare in modo sicuro nome utente / password (locale)?


106

Sto creando un'applicazione Windows, a cui devi prima accedere.
I dettagli dell'account sono costituiti da nome utente e password e devono essere salvati localmente.
È solo una questione di sicurezza, quindi altre persone che utilizzano lo stesso computer non possono vedere i dati personali di tutti.
Qual è il modo migliore / più sicuro per salvare questi dati?

Non voglio usare un database, quindi ho provato alcune cose con i file di risorse.
Ma dal momento che sono un po 'nuovo con questo, non sono del tutto sicuro di quello che sto facendo e di dove dovrei cercare una soluzione.


6
Prima di tutto, non salvare la password. Hash (possibilmente con un valore di sale) e salvarlo invece.
carlosfigueira,

"Utenti" intendi utenti regolari di Windows o qualcos'altro? (Penso che intendi dire che alcuni di voi possiedono "utenti" in quanto utente normale di Windows già non possono vedere i dati degli altri ...)
Alexei Levenkov

Ho modificato il tuo titolo. Si prega di vedere "Le domande dovrebbero includere" tag "nei loro titoli? ", Dove il consenso è "no, non dovrebbero".
John Saunders,

@ John Saunders Va bene, scusa la mia ignoranza.
Robin

2
qualsiasi soluzione finale con il codice sorgente completo?
Kiquenet

Risposte:


160

Se si intende solo verificare / convalidare il nome utente e la password immessi, utilizzare la classe Rfc2898DerivedBytes (nota anche come funzione di derivazione della chiave basata su password 2 o PBKDF2). Questo è più sicuro rispetto all'utilizzo di crittografia come Triple DES o AES perché non esiste un modo pratico per tornare dal risultato di RFC2898DerivedBytes alla password. Puoi solo passare da una password al risultato. Vedere È consentito utilizzare l'hash SHA1 della password come salt quando si ricava la chiave di crittografia e IV dalla stringa della password? per un esempio e una discussione per. Net o String crittografare / decrittografare con password c # Metro Style per WinRT / Metro.

Se si memorizza la password per il riutilizzo, ad esempio per fornirla a una terza parte, utilizzare l' API di protezione dei dati di Windows (DPAPI) . Utilizza le chiavi generate e protette dal sistema operativo e l' algoritmo di crittografia Triple DES per crittografare e decrittografare le informazioni. Ciò significa che la tua applicazione non deve preoccuparsi di generare e proteggere le chiavi di crittografia, una delle principali preoccupazioni quando si utilizza la crittografia.

In C #, usa la classe System.Security.Cryptography.ProtectedData . Ad esempio, per crittografare un dato, usa ProtectedData.Protect():

// Data to protect. Convert a string to a byte[] using Encoding.UTF8.GetBytes().
byte[] plaintext; 

// Generate additional entropy (will be used as the Initialization vector)
byte[] entropy = new byte[20];
using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
    rng.GetBytes(entropy);
}

byte[] ciphertext = ProtectedData.Protect(plaintext, entropy,
    DataProtectionScope.CurrentUser);

Memorizza l'entropia e il testo cifrato in modo sicuro, ad esempio in un file o in una chiave di registro con le autorizzazioni impostate in modo che solo l'utente corrente possa leggerli. Per accedere ai dati originali, utilizzare ProtectedData.Unprotect():

byte[] plaintext= ProtectedData.Unprotect(ciphertext, entropy,
    DataProtectionScope.CurrentUser);

Notare che esistono ulteriori considerazioni sulla sicurezza. Ad esempio, evita di memorizzare segreti come password come file string. Le stringhe sono immutabili, poiché non possono essere notificate in memoria, quindi qualcuno che guarda la memoria dell'applicazione o un dump della memoria potrebbe vedere la password. Utilizzare invece SecureString o un byte [] e ricordarsi di eliminarli o azzerarli non appena la password non è più necessaria.


Ciao, ho provato, ma ho ricevuto un errore in entropy = rng.GetBytes (20) che diceva: Impossibile convertire da int a byte []
Robin

@ CrispyGMR Ho corretto quel pezzo di codice nella risposta. Buona pesca.
akton

Molte grazie. All'inizio ho usato md5 per l'hashing, ma ero un po 'scettico al riguardo. Questo sembra molto più sicuro. Un'altra domanda però. Voglio salvare alcuni dati come questo in un file di testo. Vedo che sono solo un mucchio di caratteri casuali quando apro il mio file, ma è abbastanza sicuro per farlo? O consigli un altro modo di memorizzare i dati?
Robin

2
Sembra che la classe sia ora nota come Rfc2898DeriveBytes (lettere minuscole, .net 4.5 e 4.6) e può essere trovata qui: Namespace: System.Security.Cryptography Assembly: mscorlib (in mscorlib.dll)
Dashu

2
Molto istruttivo, tuttavia penso che lo scopo principale dell'utilizzo ProtectedDatasia così che non devo preoccuparmi di Memorizzare l'entropia e il testo cifrato in modo sicuro, ... in modo che solo l'utente corrente possa leggerlo . Penso che offra semplicità in quanto posso memorizzarli, tuttavia è conveniente e tuttavia solo CurrentUser può decrittografarlo. Anche il entropyparametro è opzionale e sembra simile a un IV in cui l'unicità conta più della segretezza. In quanto tale, il valore potrebbe probabilmente essere omesso o codificato nel programma in situazioni in cui la variazione e l'aggiornamento del testo in chiaro sono rari.
Antak

8

L'ho già usato in passato e penso che per assicurarmi che le credenziali persistano e che lo sia nel modo più sicuro

  1. puoi scriverli nel file di configurazione dell'app usando la ConfigurationManagerclasse
  2. proteggere la password utilizzando la SecureStringclasse
  3. quindi crittografandolo utilizzando gli strumenti nello Cryptographyspazio dei nomi.

Questo collegamento sarà di grande aiuto spero: Clicca qui


4

DPAPI è solo per questo scopo. Utilizzare DPAPI per crittografare la password la prima volta che l'utente accede, archiviarla in un luogo sicuro (il registro dell'utente, la directory dei dati dell'applicazione dell'utente, sono alcune scelte). Ogni volta che l'app viene avviata, controlla la posizione per vedere se la tua chiave esiste, se utilizza DPAPI per decrittografarla e consentire l'accesso, altrimenti negarla.



1

Volevo crittografare e decrittografare la stringa come stringa leggibile.

Di seguito è riportato un esempio rapido molto semplice in C # Visual Studio 2019 WinForms basato sulla risposta da @Pradip.

Fare clic con il pulsante destro del mouse su progetto> proprietà> impostazioni> creare un'impostazione usernamee password.

inserisci qui la descrizione dell'immagine

Ora puoi sfruttare le impostazioni che hai appena creato. Qui salvo il usernamee passwordma crittografa solo il passwordcampo del valore rispettabile nel user.configfile.

Esempio della stringa crittografata nel user.configfile.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <secure_password_store.Properties.Settings>
            <setting name="username" serializeAs="String">
                <value>admin</value>
            </setting>
            <setting name="password" serializeAs="String">
                <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAQpgaPYIUq064U3o6xXkQOQAAAAACAAAAAAAQZgAAAAEAACAAAABlQQ8OcONYBr9qUhH7NeKF8bZB6uCJa5uKhk97NdH93AAAAAAOgAAAAAIAACAAAAC7yQicDYV5DiNp0fHXVEDZ7IhOXOrsRUbcY0ziYYTlKSAAAACVDQ+ICHWooDDaUywJeUOV9sRg5c8q6/vizdq8WtPVbkAAAADciZskoSw3g6N9EpX/8FOv+FeExZFxsm03i8vYdDHUVmJvX33K03rqiYF2qzpYCaldQnRxFH9wH2ZEHeSRPeiG</value>
            </setting>
        </secure_password_store.Properties.Settings>
    </userSettings>
</configuration>

inserisci qui la descrizione dell'immagine

Codice completo

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace secure_password_store
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Exit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void Login_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                Properties.Settings.Default.username = textBox1.Text;
                Properties.Settings.Default.password = EncryptString(ToSecureString(textBox2.Text));
                Properties.Settings.Default.Save();
            }
            else if (checkBox1.Checked == false)
            {
                Properties.Settings.Default.username = "";
                Properties.Settings.Default.password = "";
                Properties.Settings.Default.Save();
            }
            MessageBox.Show("{\"data\": \"some data\"}","Login Message Alert",MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        private void DecryptString_Click(object sender, EventArgs e)
        {
            SecureString password = DecryptString(Properties.Settings.Default.password);
            string readable = ToInsecureString(password);
            textBox4.AppendText(readable + Environment.NewLine);
        }
        private void Form_Load(object sender, EventArgs e)
        {
            //textBox1.Text = "UserName";
            //textBox2.Text = "Password";
            if (Properties.Settings.Default.username != string.Empty)
            {
                textBox1.Text = Properties.Settings.Default.username;
                checkBox1.Checked = true;
                SecureString password = DecryptString(Properties.Settings.Default.password);
                string readable = ToInsecureString(password);
                textBox2.Text = readable;
            }
            groupBox1.Select();
        }


        static byte[] entropy = Encoding.Unicode.GetBytes("SaLtY bOy 6970 ePiC");

        public static string EncryptString(SecureString input)
        {
            byte[] encryptedData = ProtectedData.Protect(Encoding.Unicode.GetBytes(ToInsecureString(input)),entropy,DataProtectionScope.CurrentUser);
            return Convert.ToBase64String(encryptedData);
        }

        public static SecureString DecryptString(string encryptedData)
        {
            try
            {
                byte[] decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData),entropy,DataProtectionScope.CurrentUser);
                return ToSecureString(Encoding.Unicode.GetString(decryptedData));
            }
            catch
            {
                return new SecureString();
            }
        }

        public static SecureString ToSecureString(string input)
        {
            SecureString secure = new SecureString();
            foreach (char c in input)
            {
                secure.AppendChar(c);
            }
            secure.MakeReadOnly();
            return secure;
        }

        public static string ToInsecureString(SecureString input)
        {
            string returnValue = string.Empty;
            IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
            try
            {
                returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
            }
            finally
            {
                System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
            }
            return returnValue;
        }

        private void EncryptString_Click(object sender, EventArgs e)
        {
            Properties.Settings.Default.password = EncryptString(ToSecureString(textBox2.Text));
            textBox3.AppendText(Properties.Settings.Default.password.ToString() + Environment.NewLine);
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.