사용자 이름 / 암호 (로컬)를 안전하게 저장하는 방법은 무엇입니까?


106

먼저 로그인해야하는 Windows 응용 프로그램을 만들고 있습니다.
계정 세부 정보는 사용자 이름과 암호로 구성되며 로컬에 저장해야합니다.
보안 문제 일 뿐이므로 같은 컴퓨터를 사용하는 다른 사람들은 모든 사람의 개인 데이터를 볼 수 없습니다.
이 데이터를 저장하는 가장 안전한 방법은 무엇입니까?

데이터베이스를 사용하고 싶지 않아 리소스 파일로 몇 가지 시도했습니다.
그러나 나는 이것에 익숙하지 않기 때문에 내가 무엇을하고 있고 어디에서 해결책을 찾아야하는지 완전히 확신하지 못합니다.


6
우선 암호를 저장하지 마십시오. 그것을 해시하고 (아마도 소금 값으로) 저장하십시오.
carlosfigueira

"사용자"는 일반 Windows 사용자 또는 다른 것을 의미합니까? (나는 ... 당신의 일부가 서로의 데이터를 볼 수없는 이미 일반 Windows 사용자로 "사용자"자신의 뜻 생각)
알렉세이 Levenkov

제목을 수정했습니다. " 질문 제목에"태그 "를 포함해야합니까? "를 참조하십시오. 여기서 합의는 "아니오, 안됩니다"입니다.
John Saunders

@John Saunders 좋아, 실례합니다.
Robin

2
전체 소스 코드가있는 최종 솔루션이 있습니까?
Kiquenet 2013 년

답변:


160

입력 한 사용자 이름과 암호를 확인 / 검증하려는 경우 Rfc2898DerivedBytes 클래스 (암호 기반 키 파생 함수 2 또는 PBKDF2라고도 함)를 사용하십시오. RFC2898DerivedBytes 결과에서 암호로 다시 이동할 수있는 실용적인 방법이 없기 때문에 Triple DES 또는 AES와 같은 암호화를 사용하는 것보다 더 안전합니다. 암호에서 결과로만 이동할 수 있습니다. 암호 문자열에서 암호화 키 및 IV를 가져올 때 암호의 SHA1 해시를 솔트로 사용해도 괜찮습니까?를 참조하십시오 . .Net 또는 String 에 대한 예제 및 토론 은 WinRT / Metro 용 암호 c # Metro 스타일암호화 / 복호화합니다 .

타사에 제공하는 등 재사용을 위해 암호를 저장하는 경우 Windows DPAPI (데이터 보호 API)를 사용하십시오 . 이는 운영 체제 생성 및 보호 키와 Triple 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 []를 사용하고 암호가 더 이상 필요하지 않으면 즉시 폐기하거나 0으로 설정해야합니다.


안녕하세요, 저는 이것을 시도했지만 엔트로피 = rng.GetBytes (20)에서 오류가 발생했습니다. 다음과 같은 오류가 발생했습니다. int에서 byte []로 변환 할 수 없습니다.
Robin

@CrispyGMR 답변에서 해당 코드를 수정했습니다. 잘 잡았습니다.
akton

감사합니다. 처음에는 해싱에 md5를 사용했지만 약간 회의적이었습니다. 이것은 훨씬 더 안전 해 보입니다. 그래도 질문이 하나 더 있습니다. 이와 같은 데이터를 텍스트 파일에 저장하고 싶습니다. 파일을 열 때 임의의 문자가 많은 것을 알 수 있지만이 작업을 수행해도 안전합니까? 아니면 데이터를 저장하는 다른 방법을 권장합니까?
로빈

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

문자열을 읽을 수있는 문자열로 암호화하고 해독하고 싶었습니다.

다음은 .NET Core의 답변을 기반으로 한 C # Visual Studio 2019 WinForms의 매우 간단한 간단한 예제입니다 @Pradip.

프로젝트> 속성> 설정> usernamepassword설정 만들기를 마우스 오른쪽 버튼으로 클릭 합니다.

여기에 이미지 설명 입력

이제 방금 만든 설정을 활용할 수 있습니다. 저는 여기에 저장 username하고 password있지만 암호화 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.