Kullanıcı adı / şifre (yerel) güvenli bir şekilde nasıl kaydedilir?


106

Önce oturum açmanız gereken bir Windows uygulaması yapıyorum.
Hesap ayrıntıları kullanıcı adı ve paroladan oluşur ve yerel olarak kaydedilmeleri gerekir.
Bu sadece bir güvenlik meselesi, bu yüzden aynı bilgisayarı kullanan diğer kişiler herkesin kişisel verilerini göremez.
Bu verileri kaydetmenin en iyi / en güvenli yolu nedir?

Bir veritabanı kullanmak istemiyorum, bu yüzden Kaynak dosyalarıyla bazı şeyler denedim.
Ancak bu konuda biraz yeni olduğum için, ne yaptığımdan ve nerede bir çözüm aramam gerektiğinden tam olarak emin değilim.


6
Öncelikle şifreyi kaydetmeyin. Karıştırın (muhtemelen bir tuz değeri ile) ve bunun yerine kaydedin.
carlosfigueira

"Kullanıcılar", normal Windows kullanıcıları mı yoksa başka bir şey mi? (Sanırım, normal Windows kullanıcısı zaten birbirinizin verilerini göremediği için bazılarınızın "kullanıcılarına" sahip olduğunu kastediyorsunuz ...)
Alexei Levenkov

Başlığınızı düzenledim. Lütfen " Sorular başlıklarında" etiketler " içermeli mi? " Bölümüne bakın , burada fikir birliği "hayır, olmamalıdır".
John Saunders

@John Saunders Tamam, cehaletimi bağışlayın.
Robin

2
tam kaynak kodlu herhangi bir nihai çözüm var mı?
Kiquenet

Yanıtlar:


160

Yalnızca girilen kullanıcı adını ve parolayı doğrulayacak / doğrulayacaksanız, Rfc2898DerivedBytes sınıfını (Parola Tabanlı Anahtar Türetme İşlevi 2 veya PBKDF2 olarak da bilinir) kullanın. Bu, Triple DES veya AES gibi şifreleme kullanmaktan daha güvenlidir çünkü RFC2898DerivedBytes'in sonucundan parolaya geri dönmenin pratik bir yolu yoktur. Yalnızca bir paroladan sonuca gidebilirsiniz. Bkz . Şifre dizesinden şifreleme anahtarı ve IV türetirken SHA1 şifre karmasını bir tuz olarak kullanmak uygun mudur? .Net veya String şifreleme / şifre çözme için bir örnek ve tartışma için c # WinRT / Metro için Metro Stili .

Parolayı üçüncü bir tarafa sağlamak gibi yeniden kullanım için depoluyorsanız, Windows Veri Koruma API'sini (DPAPI) kullanın . Bu, bilgileri şifrelemek ve şifresini çözmek için işletim sistemi tarafından üretilen ve korunan anahtarları ve Üçlü DES şifreleme algoritmasını kullanır. Bu, uygulamanızın şifreleme anahtarlarını oluşturma ve koruma konusunda endişelenmesine gerek olmadığı anlamına gelir; bu, kriptografi kullanırken büyük bir endişe kaynağıdır.

C # 'da System.Security.Cryptography.ProtectedData sınıfını kullanın . Örneğin, bir veri parçasını şifrelemek için şunu kullanın 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);

Entropi ve şifreli metni, yalnızca geçerli kullanıcının okuyabilmesi için izinler ayarlanmış bir dosya veya kayıt defteri anahtarı gibi güvenli bir şekilde saklayın. Orijinal verilere erişmek için şunu kullanın ProtectedData.Unprotect():

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

Ek güvenlik hususları olduğunu unutmayın. Örneğin, parolalar gibi sırları string. Dizeler değişmezdir, bellekte bildirilemezler, bu nedenle uygulamanın belleğine veya bellek dökümüne bakan biri parolayı görebilir. Kullanım SecureString veya bayt [] yerine ve imha etme hatırlamak ya da en kısa şifre artık ihtiyaç olarak sıfır.


Merhaba, bunu denedim, ancak entropy = rng.GetBytes (20) 'de şunu söyleyen bir hata aldım: int'ten bayta dönüştürülemiyor []
Robin

@CrispyGMR Yanıttaki o kod parçasını düzelttim. İyi yakalama.
akton

Çok teşekkürler. Başlangıçta hashing için md5 kullandım, ancak bu konuda biraz şüpheliydim. Bu çok daha güvenli görünüyor. Yine de bir soru daha. Bunun gibi bazı verileri bir metin dosyasına kaydetmek istiyorum. Dosyamı açtığımda bunun sadece rastgele bir karakter olduğunu görüyorum, ancak bunu yapmak için yeterince güvenli mi? Veya verileri saklamanın başka bir yolunu mu önerirsiniz?
Robin

2
Görünüşe göre sınıf artık Rfc2898DeriveBytes (küçük harfler, .net 4.5 ve 4.6) olarak biliniyor ve burada bulunabilir: Ad Alanı: System.Security.Cryptography Assembly: mscorlib (mscorlib.dll içinde)
Dashu

2
Çok bilgilendirici, ancak bence tüm kullanım ProtectedDataamacı , entropiyi ve şifreli metni güvenli bir şekilde saklamak için endişelenmeme gerek kalmamasıdır , ... böylece sadece mevcut kullanıcı okuyabilir . Sanırım onları saklayabildiğim için basitlik sunuyor ancak kullanışlı ve yine de sadece CurrentUser şifresini çözebilir. entropyParametresi de isteğe bağlı ve özgünlüğü daha gizlilik daha önemli bir IV benzer görünür. Bu nedenle, düz metnin varyasyonunun ve güncellemesinin seyrek olduğu durumlarda, değer muhtemelen programa dahil edilmeyebilir veya kodlanmış olabilir.
antak

8

Bunu daha önce kullandım ve kimlik bilgilerinin kalıcı olduğundan emin olmak için ve en güvenli şekilde

  1. ConfigurationManagersınıfı kullanarak bunları uygulama yapılandırma dosyasına yazabilirsiniz
  2. SecureStringsınıfı kullanarak parolanın güvenliğini sağlamak
  3. daha sonra Cryptographyad alanındaki araçları kullanarak şifreleme .

Bu bağlantı çok yardımcı olacaktır umarım: Buraya tıklayın


4

DPAPI sadece bu amaç içindir. Kullanıcı ilk kez girdiğinde parolayı şifrelemek için DPAPI kullanın, parolayı güvenli bir yerde saklayın (Kullanıcının kayıt defteri, Kullanıcının uygulama verileri dizini, bazı seçeneklerdir). Uygulama her başlatıldığında, anahtarınızın var olup olmadığını görmek için konumu kontrol edin, şifresini çözmek ve erişime izin vermek için DPAPI kullanıyorsa, aksi takdirde reddedin.



1

Dizeyi okunabilir bir dize olarak şifrelemek ve şifresini çözmek istedim.

İşte C # Visual Studio 2019 WinForms'ta @Pradip.

Sağ tıklayın proje> özellikler> ayarlar> Bir usernameve passwordayar oluştur .

görüntü açıklamasını buraya girin

Şimdi, az önce oluşturduğunuz bu ayarlardan yararlanabilirsiniz. Burada kaydediyorum usernameve passwordancak dosyadaki passwordsaygın değer alanını şifreliyorum user.config.

user.configDosyadaki şifrelenmiş dizeye örnek .

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

görüntü açıklamasını buraya girin

Tam Kod

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);
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.