ユーザー名/パスワード(ローカル)を安全に保存する方法は?


106

最初にログインする必要があるWindowsアプリケーションを作成しています。
アカウントの詳細はユーザー名とパスワードで構成され、ローカルに保存する必要があります。
これはセキュリティの問題なので、同じコンピューターを使用している他の人がすべての人の個人データを見ることができません。
このデータを保存するための最良/最も安全な方法は何ですか?

データベースを使いたくないので、Resourceファイルをいくつか試してみました。
しかし、私はこれに少し慣れているので、自分が何をしているか、どこで解決策を探すべきか、完全にはわかりません。


6
まず、パスワードを保存しないでください。(おそらくソルト値で)ハッシュして、代わりに保存します。
カルロスフィゲイラ2012

「ユーザー」とは、通常のWindowsユーザーまたは他の何かを意味しますか?(私はあなたが正規のWindowsユーザーがすでに互いのデータを見ることができないのであなたの何人かが「ユーザー」を所有していることを意味すると思います...)
Alexei Levenkov

タイトルを編集しました。コンセンサスが「いいえ、彼らはすべきではない」である「質問にはタイトルに「タグ」を含める必要がありますか?」を参照してください。
John Saunders

@ジョン・サンダースは申し分なく、私の無知を許しなさい。
Robin

2
完全なソースコードを使用した最終的な解決策はありますか?
Kiquenet 2013

回答:


160

入力したユーザー名とパスワードを確認/検証するだけの場合は、Rfc2898DerivedBytesクラス(パスワードベースの鍵導出関数2またはPBKDF2とも呼ばれます)を使用します。これは、RFC2898DerivedBytesの結果からパスワードに戻る実際的な方法がないため、Triple DESやAESのような暗号化を使用するよりも安全です。あなたはパスワードから結果までしか行くことができません。参照は、それがパスワード文字列から暗号化キーとIVを導出する際に塩として、パスワードのSHA1ハッシュを使用しても大丈夫ですか?.NetまたはStringの例とディスカッションについては、WinRT / Metroのパスワードc#Metro Style暗号化/復号化してください。

サードパーティへの提供など、再利用のためにパスワードを保存する場合は、Windowsデータ保護API(DPAPI)を使用します。これは、オペレーティングシステムで生成および保護されたキーと、トリプルDES暗号化アルゴリズムを使用して、情報を暗号化および復号化します。つまり、アプリケーションは、暗号化を使用する際の主な懸念事項である暗号化キーの生成と保護について心配する必要がありません。

C#では、System.Security.Cryptography.ProtectedDataクラスを使用します。たとえば、データの一部を暗号化するには、次を使用します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);

エントロピーと暗号文を、現在のユーザーだけが読み取ることができるようにアクセス許可が設定されたファイルまたはレジストリキーなどに安全に保存します。元のデータにアクセスするには、次を使用しますProtectedData.Unprotect()

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

セキュリティに関する追加の考慮事項があることに注意してください。たとえば、パスワードなどのシークレットをとして保存しないでくださいstring。文字列は不変であり、メモリでは通知されないため、アプリケーションのメモリまたはメモリダンプを見ている人がパスワードを見る可能性があります。代わりにSecureStringまたはbyte []を使用し、パスワードが不要になったらすぐに破棄するかゼロにすることを忘れないでください。


こんにちは、私はこれを試しましたが、entropy = rng.GetBytes(20)でエラーが発生しました:intからbyte []に​​変換できません
Robin

@CrispyGMR答えのコードを修正しました。良いキャッチ。
akton

どうもありがとう。最初はハッシュにmd5を使用しましたが、それについては少し懐疑的でした。これははるかに安全なようです。もう一つ質問があります。このようなデータをテキストファイルに保存したいのですが。ファイルを開いたとき、ランダムな文字の集まりであることがわかりましたが、これで十分ですか?または、データを保存する別の方法をお勧めしますか?
Robin

2
クラスは現在Rfc2898DeriveBytes(小文字、.net 4.5および4.6)と呼ばれ、ここにあります:名前空間:System.Security.Cryptographyアセンブリ:mscorlib(mscorlib.dll内)
Dashu

2
非常に有益ですが、使用の全体的なポイントは、エントロピーと暗号化テキストを安全に保存ProtectedDataすることについて心配する必要がないようになっていると思います。私はそれらを保存できるという点で単純さを提供すると思いますが、便利であり、それでもCurrentUserだけがそれを復号化できます。entropyパラメータはまた、オプションで、独自性がより機密性よりも重要IVのように表示されます。そのため、平文のバリエーションや更新が頻繁でない状況では、値がおそらく省略されるか、プログラムにハードコードされる可能性があります。
antak

8

私はこれを以前に使用したことがありますが、資格情報が確実に持続し、最良の安全な方法で

  1. ConfigurationManagerクラスを使用してそれらをアプリ構成ファイルに書き込むことができます
  2. SecureStringクラスを使用してパスワードを保護する
  3. 次に、Cryptography名前空間のツールを使用して暗号化します。

このリンクは私が望む大きな助けになります:ここをクリックしてください


4

DPAPIはこの目的のためだけです。ユーザーが初めて入力するときにDPAPIを使用してパスワードを暗号化し、安全な場所に保存します(ユーザーのレジストリ、ユーザーのアプリケーションデータディレクトリなどを選択できます)。アプリが起動するたびに、場所を確認して、キーが存在するかどうかを確認します。キーがDPAPIを使用して解読してアクセスを許可する場合は、それ以外の場合は拒否します。



1

文字列を読み取り可能な文字列として暗号化および復号化したかったのです。

これは、C#Visual Studio 2019 WinFormsでの非常に単純な簡単な例@Pradipです。

右クリックプロジェクト>プロパティ>設定>を作成usernameしてpassword設定を。

ここに画像の説明を入力してください

これで、作成したばかりの設定を活用できます。ここで私は、保存usernamepasswordしか暗号化passwordして、それの立派値フィールド内をuser.configファイル。

user.configファイル内の暗号化された文字列の例。

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

ここに画像の説明を入力してください

完全なコード

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);
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.