비밀번호 저장에 관한 질문입니다.

  • 현재 작성한 코드 중 문제가 되는 부분
    소스파일에 지역변수, 평문으로 저장되어 있어요.
  • 기대하는 동작
    안전하게 저장하고, 짧은 시간동안만 메모리에 있으면 좋겠습니다.

KISA 시큐어코딩 가이드 라고 있었던 기억이 있어서 인터넷을 검색해 보니 몇 년 지난 것만 보이네요. KISA 홈페이지 링크는 주소가 바뀌었다고 나오구요. 매년 발행하는 것이 아닌지 모르겠지만요.
이런 것도 국제표준 같은 것이 있을까요?

3 Likes

국제 표준이라기 보다, 권고안 (Best Practice) 정도는 통용되는 것 같습니다.

흥미로운 질문이어서 좀 찾아보니 일단 Microsoft에서 정리한 시큐어 코딩 가이드라인이 있네요. 이건 한 번 읽어보시는 것도 좋을 것 같습니다.

말씀하신 "짧은 시간 동안만 메모리에 저장되는 데이터"에 부합하는 유틸리티 클래스로 SecureString이 종종 언급되곤 합니다.

그 외에도 최근에 본 것으로 인텔 SGX가 있었습니다.

인텔 SGX(Software Guard Extension)란? 개념정리 - Easy is Perfect(enclave,%EC%88%98%20%EC%97%86%EB%8F%84%EB%A1%9D%20%ED%95%98%EB%8A%94%20%EA%B8%B0%EC%88%A0%EC%9E%85%EB%8B%88%EB%8B%A4.

최신 버전의 인텔 프로세서에 탑재되는 SGX Enclave 기능을 사용하는 사례도 있긴 합니다. 링크하는 예제는 보편적으로 인텔 프로세서가 많이 쓰인다고 말할 수 있는 Windows와 .NET Framework 기반 실행 환경에서 SGX를 사용하는 예제를 소개하는 인텔의 공식 페이지입니다.

https://www.intel.com/content/www/us/en/developer/articles/technical/csharp-application-with-intel-software-guard-extensions.html

닷넷 코어와 닷넷 5 이후에서 리눅스 기반으로 인텔 SGX를 사용한다면 아마 제일 효과적인 시나리오는 인텔 SGX를 지원하는 Azure나 AWS 인스턴스 위에서 Docker 애플리케이션을 배포하는 사례가 있을 것 같습니다. Azure의 경우를 참고 자료로 링크해드립니다.

https://azure.microsoft.com/ko-kr/updates/intel-sgx-based-confidential-computing-vms-now-available-on-azure-dedicated-hosts/

5 Likes

답변 감사드립니다.
덕분에 흥미로운 내용들 잘 보았습니다. 읽는데 좀 힘들긴 했지만요…
저는 비밀번호는 암호화해서 파일에 저장하고, 복호화 할 땐 SecureString을 사용하려고 합니다. AMD 사용자도 고려해야 해서요. 코드에 문제는 없을지 봐 주시길 부탁드립니다.

public byte[] AESEncrypt256(byte[] plainBytes, string password)
{
    ...

    byte[]? encrypted = null;
    using (var memoryStream = new MemoryStream())
    {
        using (var cryptoStream = new CryptoStream(memoryStream, _aes.CreateEncryptor(secretKey, iv), CryptoStreamMode.Write))
        {
            cryptoStream.Write(plainBytes, 0, plainBytes.Length);
        }
        encrypted = memoryStream.ToArray();
    }
    return encrypted;
}

public SecureString AESDecrypt256(byte[] encryptedBytes, string password)
{
    ...
    
    var secString = new SecureString();
    using (var memStream = new MemoryStream())
    {
        using (var cryptoStream = new CryptoStream(memStream, _aes.CreateDecryptor(secretKey, iv), CryptoStreamMode.Write))
        {
            cryptoStream.Write(encryptedBytes, 0, encryptedBytes.Length);
        }
        var decrypted = memStream.ToArray();
        var charArray = Encoding.UTF8.GetString(decrypted).ToCharArray();
        Array.ForEach(charArray, c => secString.AppendChar(c));
        Array.Clear(charArray);
        Array.Clear(decrypted);
    }
    return secString;
}
2 Likes