.NET에서 언제 SecureString이 필요합니까?


179

.NET의 SecureString의 목적을 파악하려고합니다. MSDN에서 :

System.String 클래스의 인스턴스는 변경할 수 없으며 더 이상 필요하지 않은 경우 가비지 수집을 위해 프로그래밍 방식으로 예약 할 수 없습니다. 즉, 인스턴스는 작성된 후 읽기 전용이며 인스턴스가 컴퓨터 메모리에서 삭제 될시기를 예측할 수 없습니다. 따라서 String 개체에 암호, 신용 카드 번호 또는 개인 데이터와 같은 중요한 정보가 포함 된 경우, 응용 프로그램이 컴퓨터 메모리에서 데이터를 삭제할 수 없기 때문에 사용 후 정보가 노출 될 위험이 있습니다.

SecureString 개체는 텍스트 값이 있다는 점에서 String 개체와 유사합니다. 그러나 SecureString 개체의 값은 자동으로 암호화되어 응용 프로그램이 읽기 전용으로 표시 될 때까지 수정 될 수 있으며 응용 프로그램이나 .NET Framework 가비지 수집기에 의해 컴퓨터 메모리에서 삭제할 수 있습니다.

SecureString 인스턴스의 값은 인스턴스가 초기화되거나 값이 수정 될 때 자동으로 암호화됩니다. 애플리케이션은 MakeReadOnly 메소드를 호출하여 인스턴스를 변경할 수 없게하고 추가 수정을 방지 할 수 있습니다.

자동 암호화가 큰 보상입니까?

그리고 왜 내가 말할 수는 없습니다.

SecureString password = new SecureString("password");

대신에

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

SecureString의 어떤 측면이 누락 되었습니까?


3
11 년 후 MS는 더 이상 SecureString새로운 개발을 권장하지 않습니다 : github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Matt Thomas

답변:


4

SecureString 사용을 중단합니다. PG 사람들이 지원을 중단하는 것 같습니다. https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring 미래에도 가능할 것입니다 .

.NET Core의 모든 플랫폼에서 SecureString에서 암호화를 제거해야합니다.-SecureString을 폐기해야합니다. .NET Core에서 SecureString을 노출하지 않아야합니다.


1
링크가 죽었하지만 계속 나타납니다 : docs.microsoft.com/en-us/dotnet/api/... "망가 사용 자격 증명"경로 앞으로 execpt에 매우 약한 지침 .. - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. 인증서의 개인 키를 보호하기 위해 비밀번호를 감히 사용하지 마십시오!
felickz

1
11 년이 지난 지금이 ​​답변은 '새로운'올바른 답변으로 보입니다. 링크가 오래되지 않은 것처럼 보이지만 MS의 지침은 다음과 같습니다. SecureString을 사용해서는 안됩니다
Richard Morgan

109

현재 사용하고있는 프레임 워크의 일부 SecureString:

주요 목적은 공격 표면을 제거하는 것이 아니라 감소시키는 것입니다. SecureStrings가비지 콜렉터가 RAM을 이동하거나 복사하지 않습니다. 또한 일반 텍스트가 스왑 파일이나 코어 덤프에 기록되지 않도록합니다. 암호화는 난독 화와 비슷하며 해커를 막을 수는 없지만 암호화 및 해독에 사용되는 대칭 키 를 찾을 수 있습니다.

다른 사람들이 말했듯이 SecureString문자별로 문자 를 작성 해야하는 이유 는 그렇지 않은 첫 번째 명백한 결함 때문입니다. 아마도 비밀 값을 이미 일반 문자열로 가지고 있기 때문에 요점은 무엇입니까?

SecureStrings는 Chicken-and-Egg 문제를 해결하는 첫 번째 단계이므로, 대부분의 현재 시나리오에서는이를 사용하기 위해 일반 문자열로 다시 변환해야하지만 프레임 워크에서의 존재는 이제 미래-적어도 프로그램이 약한 링크가 될 필요가없는 지점까지.


2
ProcessStartInfo의 Password 속성을보고 그것을 가로 질러 뛰어 다녔습니다. 유형에주의를 기울이지 않아도 컴파일러가 짖을 때까지 일반 문자열로 설정했습니다.
Richard Morgan

SecureString은 DPAPI를 기반으로하기 때문에 대칭 암호화 키를 찾기가 쉽지 않습니다. DPAPI를 기반으로하기 때문에 키가 일반 텍스트로 정확하게 저장되지 않습니다.
AviD

1
또한 스토리지의 암호화를 대체하는 것이 아니라 불변의 관리되는 .NET 문자열에 대한 해결 방법이기 때문에 치킨과 계란 문제가 아닙니다.
AviD

2
"비밀 값을 이미 일반 문자열로 가지고있을 것입니다. 그래서 요점은 무엇입니까?" 이 질문에 대한 답변이 있습니까? 암호를 메모리에 장기간 보관하려는 경우 "아무것보다 나은"솔루션 인 것 같습니다.
xr280xr

2
사용법에 대한 간단한 코드 예제가 거의 없습니까? 언제 어떻게 사용하는지 더 잘 이해할 것이라고 믿습니다.
codea

37

편집 : SecureString을 사용하지 마십시오

현재 지침에 따르면 수업을 사용해서는 안됩니다. 자세한 내용은 다음 링크에서 확인할 수 있습니다. https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

기사에서 :

DE0001 : SecureString을 사용하지 않아야합니다

자극

  • SecureString프로세스 메모리에 비밀을 일반 텍스트로 저장하지 않도록 하는 것이 목적입니다 .
  • 그러나 Windows에서도 SecureStringOS 개념으로 존재하지 않습니다.
    • 그것은 단지 일반 텍스트를 점점 짧게 만듭니다; .NET은 여전히 ​​문자열을 일반 텍스트 표현으로 변환해야하기 때문에 완전히 막을 수는 없습니다.
    • 이점은 일반 텍스트 표현이 인스턴스의 역할을하지 않는다는 것 System.String입니다. 기본 버퍼의 수명이 짧습니다.
  • 배열의 내용은 .NET Framework를 제외하고 암호화되지 않습니다.
    • .NET Framework에서 내부 문자 배열의 내용은 암호화됩니다. .NET은 API 누락 또는 키 관리 문제로 인해 모든 환경에서 암호화를 지원하지 않습니다.

추천

SecureString새로운 코드 에는 사용하지 마십시오 . 코드를 .NET Core로 포팅 할 때 배열의 내용이 메모리에 암호화되어 있지 않은 것을 고려하십시오.

자격 증명을 처리하는 일반적인 방법은 자격 증명을 피하고 대신 인증서 또는 Windows 인증과 같은 다른 인증 방법을 사용하는 것입니다.

편집 종료 : 아래의 원본 요약

많은 훌륭한 답변; 다음은 논의 된 내용에 대한 간략한 개요입니다.

Microsoft는 신용 카드, 암호 등의 중요한 정보로 더 나은 보안을 제공하기 위해 SecureString 클래스를 구현했습니다. 자동으로 다음을 제공합니다.

  • 암호화 (메모리 덤프 또는 페이지 캐싱의 경우)
  • 메모리에 고정
  • 읽기 전용으로 표시하는 기능 (추가 수정 방지)
  • 상수 문자열을 전달하지 않도록하여 안전한 구조

현재 SecureString은 사용이 제한되어 있지만 앞으로 더 나은 채택을 기대합니다.

이 정보를 기반으로 SecureString의 생성자는 문자열을 가져 와서 문자 배열로 슬라이스하지 않아야합니다.

추가 정보:

  • .NET 보안 블로그 의 게시물 은 여기에서 다루는 것과 거의 동일합니다.
  • 또 다른 사람은 그것을 다시 방문하고 SecureString의 내용을 덤프 할 수있는 도구를 언급했습니다.

편집 : 많은 사람들에게 좋은 정보가 있기 때문에 최상의 답변을 선택하기가 어렵다는 것을 알았습니다. 너무 나빠서 보조 답변 옵션이 없습니다.


19

짧은 답변

왜 난 그냥 말할 수 없습니다 :

SecureString password = new SecureString("password");

지금 당신은 password메모리에 있기 때문에 ; 그것을 닦을 방법이 없습니다-정확히 요점입니다. SecureString .

긴 답변

SecureString 이 존재 하는 이유 는 중요한 데이터를 다룰ZeroMemory 를 사용 하여 중요한 데이터를 지울 수 없기 때문 입니다. 존재하는 문제를 해결하기 위해 존재합니다 .CLR로 .

일반 네이티브 응용 프로그램에서는 다음을 호출합니다 SecureZeroMemory.

메모리 블록을 0으로 채 웁니다.

참고 : SecureZeroMemory이다는 동일 ZeroMemory, 컴파일러를 제외하고 멀리 최적화되지 않습니다.

문제는 전화를 걸 거나 .NET 내부에서 할 수 없다는 것 입니다. 그리고 .NET에서 문자열은 변경할 수 없습니다. 다른 언어에서와 같이 문자열의 내용을 덮어 쓸 수도 없습니다 .ZeroMemorySecureZeroMemory

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

그래서 당신은 무엇을 할 수 있습니까? .NET에서 암호 나 신용 카드 번호를 메모리에서 지울 수있는 기능을 어떻게 제공합니까?

그것을 할 수있는 유일한 방법은 일부 문자열을 배치하는 것입니다 네이티브 는 메모리 블록, 후 전화 ZeroMemory. 다음과 같은 기본 메모리 객체 :

  • BSTR
  • HGLOBAL
  • CoTaskMem 비 관리 메모리

SecureString은 잃어버린 능력을 돌려줍니다.

.NET에서 문자열을 완료하면 문자열을 지울 수 없습니다.

  • 그들은 불변이다. 당신은 그들의 내용을 덮어 쓸 수 없습니다
  • 당신 Dispose은 그들 중 누구도
  • 그들의 청소는 가비지 수집기의 자비에 있습니다

SecureString은 문자열 안전을 전달하는 방법으로 존재하며 필요할 때 정리를 보장 할 수 있습니다.

당신은 질문을했다 :

왜 난 그냥 말할 수 없습니다 :

SecureString password = new SecureString("password");

지금 당신은 password메모리에 있기 때문에 ; 닦을 방법이 없습니다. CLR이 해당 메모리를 재사용하기로 결정할 때까지 중단됩니다. 당신은 우리가 시작한 곳으로 우리를 바로 돌려 놓았습니다. 우리가 제거 할 수없는 암호가있는 실행중인 응용 프로그램과 메모리 덤프 (또는 프로세스 모니터)가 암호를 볼 수있는 곳.

SecureString은 Data Protection API를 사용하여 메모리에 암호화 된 문자열을 저장합니다. 그런 식으로 스왑 파일, 크래시 덤프 또는 로컬 변수 창에 문자열이 존재하지 않아야합니다.

비밀번호는 어떻게 읽습니까?

그렇다면 질문은 : 문자열과 어떻게 상호 작용합니까? 당신은 절대적으로 다음 과 같은 방법을 원하지 않습니다 :

String connectionString = secureConnectionString.ToString()

이제 시작한 곳으로 바로 돌아갈 수 있기 때문에 제거 할 수없는 비밀번호입니다. 당신은 할 강제 제대로 민감한 문자열을 처리하기 위해 개발자 - 너무 것을 할 수 메모리에서 닦아.

따라서 .NET은 SecureString을 관리되지 않는 메모리에 마샬링하는 세 가지 편리한 도우미 함수를 제공합니다.

문자열을 관리되지 않는 메모리 얼룩으로 변환하고 처리 한 다음 다시 지 웁니다.

일부 API는 SecureStrings를 허용 합니다. 예를 들어 ADO.net 4.5에서 SqlConnection.CredentialSqlCredential 세트를 가져옵니다 .

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

연결 문자열 내에서 비밀번호를 변경할 수도 있습니다.

SqlConnection.ChangePassword(connectionString, cred, newPassword);

그리고 .NET 내부에는 호환성을 위해 일반 문자열을 계속 수용 한 다음 SecureString으로 빠르게 전환합니다.

SecureString에 텍스트를 넣는 방법?

여전히 문제가 남아 있습니다.

처음에 SecureString에 비밀번호를 얻으려면 어떻게해야합니까?

이것은 도전이지만, 요점은 보안에 대해 생각하게하는 것입니다.

때로는 기능이 이미 제공되어 있습니다. 예를 들어, WPF PasswordBox 컨트롤은 입력 한 암호를 SecureString으로 직접 반환 할 수 있습니다.

PasswordBox.SecurePassword 속성

PasswordBox가 현재 보유하고있는 비밀번호SecureString 으로 가져옵니다 .

이것은 원시 문자열을 전달하는 데 사용되는 모든 곳에서 SecureString이 String과 호환되지 않는다고 불평하는 유형 시스템을 가지고 있기 때문에 유용합니다. SecureString을 다시 일반 문자열로 변환하기 전에 가능한 한 오래 가고 싶습니다.

SecureString 변환은 충분히 쉽습니다.

  • SecureStringToBSTR
  • PtrToStringBSTR

에서와 같이 :

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

그들은 단지 당신이 그것을하고 싶지 않습니다.

그러나 SecureString에 문자열을 어떻게 가져 옵니까? 글쎄, 당신이해야 할 일은 처음에 문자열 에 암호를 갖는 것을 멈추는 것입니다. 당신은 다른 것을 가지고 있어야했습니다 . 심지어Char[]배열 도움이 될 것입니다.

각 문자를 추가 할 수 있습니다 때이다 그리고 이 완료되면 일반 텍스트를 닦아 :

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

초기화 할 수 있는 일부 메모리에 비밀번호가 저장되어 있어야합니다 . 거기에서 SecureString에로드하십시오.


tl; dr : SecureStringZeroMemory 와 동등한 기능을 제공합니다. .

어떤 사람들은에 지점이 표시되지 않는 장치가 잠겨있을 때 메모리에서 사용자의 암호를 닦아 , 또는 닦아 they'authenticated 후 메모리에서 닦아 키 입력을 . 그 사람들은 SecureString을 사용하지 않습니다.


14

현재 버전의 프레임 워크에서 SecureString을 현명하게 사용할 수있는 시나리오는 거의 없습니다. 관리되지 않는 API와의 상호 작용에만 유용합니다. Marshal.SecureStringToGlobalAllocUnicode를 사용하여 마샬링 할 수 있습니다.

System.String으로 /에서 변환하자마자 그 목적을 잃었습니다.

MSDN 샘플 은 콘솔 입력에서 한 번에 한 문자 씩 SecureString을 생성하고 보안 문자열을 관리되지 않는 API로 전달합니다. 오히려 복잡하고 비현실적입니다.

향후 버전의 .NET은 SecureString을 더 많이 지원하여보다 유용하게 사용할 수 있습니다. 예를 들면 다음과 같습니다.

  • SecureString Console.ReadLineSecure () 또는 샘플에서 모든 복잡한 코드없이 SecureString으로 콘솔 입력을 읽는 것과 유사합니다.

  • 암호를 안전하게 입력 할 수 있도록 TextBox.Text 속성을 보안 문자열로 저장하는 WinForms TextBox 대체.

  • 비밀번호를 SecureString으로 전달할 수있는 보안 관련 API의 확장.

위의 내용이 없으면 SecureString의 값이 제한됩니다.


12

플랫 인스턴스화 대신 문자를 추가 해야하는 이유는 SecureString의 생성자에 "password"를 전달하는 배경에서 "password"문자열을 메모리에 넣어 보안 문자열의 목적을 무효화하기 때문입니다.

덧붙여서 당신은 한 번에 한 문자 만 메모리에 두어 서로 인접하지 않기 때문에 원래의 문자열을 재구성하기가 훨씬 어려워집니다. 나는 여기에 틀릴 수 있지만 그것이 나에게 설명 된 방법입니다.

이 클래스의 목적은 메모리 덤프 또는 유사한 도구를 통해 보안 데이터가 노출되지 않도록하는 것입니다.


11

MS는 서버 (데스크톱 등)가 충돌하는 특정 인스턴스에서 런타임 환경이 메모리에있는 내용의 내용을 노출시키는 메모리 덤프를 수행 할 때가 있음을 발견했습니다. 보안 문자열은 공격자가 문자열의 내용을 검색 할 수 없도록 메모리에 암호화합니다.


5

SecureString의 가장 큰 장점 중 하나는 페이지 캐싱으로 인해 데이터가 디스크에 저장 될 가능성이 없다는 것입니다. 메모리에 비밀번호가 있고 큰 프로그램 또는 데이터 세트를로드하는 경우 프로그램의 메모리가 부족하면 비밀번호가 스왑 파일에 기록 될 수 있습니다. SecureString을 사용하면 최소한 데이터가 디스크에 무한정 텍스트로 표시되지 않습니다.


4

문자열이 안전하기 때문에 해커가 읽을 수 없어야하기 때문입니다. 문자열로 초기화하면 해커가 원래 문자열을 읽을 수 있습니다.


4

설명에서 알 수 있듯이 값은 암호화되어 저장되므로 프로세스의 메모리 덤프는 문자열 값을 공개하지 않습니다 (일부 심각한 작업이 없음).

당신이 있기 때문에 그냥 상수 문자열에서 SecureString을 구성 할 수없는 이유는 것입니다 메모리에 문자열의 암호화되지 않은 버전이있다. 문자열을 조각으로 만들도록 제한하면 전체 문자열을 한 번에 메모리에 넣을 위험이 줄어 듭니다.


2
만약 그들이 일정한 문자열로부터 생성을 제한한다면, foreach (char c in "password".ToCharArray ()의 줄)는 그것을 패배시킬 것입니다. pass.AppendChar ( 'p');이어야합니다. pass.AppendChar ( 'a'); 등?
Richard Morgan

예, SecureString이 제공하는 작은 보호 기능을 쉽게 버릴 수 있습니다. 그들은 발로 자신을 완전히 쏘기가 어려워지고 있습니다. 분명히 SecureString에 값을 가져오고 내릴 수있는 방법이 있어야합니다. 그렇지 않으면 아무것도 사용할 수 없습니다.
Mark Bessey

1

또 다른 사용 사례는 POS (Payment Application)로 작업하고 있으며 개발자가 신중하기 때문에 민감한 데이터를 저장하기 위해 변경 불가능한 데이터 구조사용할 수없는 경우 입니다. 예를 들어, 민감한 카드 데이터 또는 인증 메타 데이터를 변경 불가능한 문자열에 저장하면이 데이터를 메모리에서 폐기 한 후 상당한 시간 동안 사용할 수있는 경우가 항상 있습니다. 간단히 덮어 쓸 수 없습니다. 이러한 민감한 데이터가 메모리에 암호화되어 보관되는 또 다른 큰 장점.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.