그것의 선택 System.String을 작성하여 SecureString의 보안을 해제에 대한 모든 예약 제외하고는 , 어떻게 수행 할 수 있습니다?
일반 System.Security.SecureString을 System.String으로 어떻게 변환 할 수 있습니까?
SecureString에 익숙한 많은 분들이 SecureString을 일반 .NET 문자열로 변환해서는 안된다고 대답 할 것입니다. 모든 보안 보호를 제거하기 때문입니다. 알아 . 그러나 지금 당장 내 프로그램은 어쨌든 일반 문자열로 모든 작업을 수행하고 있으며 보안을 강화하려고 노력하고 있으며 SecureString을 반환하는 API를 사용할 예정이지만 보안을 강화하기 위해 사용 하려는 것은 아닙니다 .
Marshal.SecureStringToBSTR에 대해 알고 있지만 BSTR을 가져 와서 System.String을 만드는 방법을 모르겠습니다.
내가 왜 이것을하고 싶은지 알고 싶어하는 사람들을 위해, 나는 사용자로부터 암호를 가져 와서 웹 사이트에 로그인하기 위해 html 양식 POST로 제출하고 있습니다. 그래서 … 이것은 정말로 관리되고 암호화되지 않은 버퍼로 이루어져야합니다. 관리되지 않고 암호화되지 않은 버퍼에 액세스 할 수 있다면 네트워크 스트림에서 바이트 단위 스트림 쓰기를 수행 할 수 있다고 생각하며 이것이 암호를 전체적으로 안전하게 유지하기를 바랍니다. 이러한 시나리오 중 하나 이상에 대한 답변을 기대하고 있습니다.
답변
System.Runtime.InteropServices.Marshal
수업 사용 :
String SecureStringToString(SecureString value) {
IntPtr valuePtr = IntPtr.Zero;
try {
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
return Marshal.PtrToStringUni(valuePtr);
} finally {
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
}
관리되는 문자열 객체를 생성하지 않으려면 다음을 사용하여 원시 데이터에 액세스 할 수 있습니다 Marshal.ReadInt16(IntPtr, Int32)
.
void HandleSecureString(SecureString value) {
IntPtr valuePtr = IntPtr.Zero;
try {
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
for (int i=0; i < value.Length; i++) {
short unicodeChar = Marshal.ReadInt16(valuePtr, i*2);
// handle unicodeChar
}
} finally {
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
}
답변
분명히 이것이 SecureString의 전체 목적을 무너 뜨리는 방법을 알고 있지만 어쨌든 다시 언급하겠습니다.
한 줄짜리를 원하면 다음을 시도하십시오. (. NET 4 이상 만 해당)
string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;
여기서 securePassword는 SecureString입니다.
답변
댕. 바로 게시 한 후이 난에 응답 깊은 발견 이 기사를 . 그러나 누군가가이 메서드가 노출하는 관리되지 않는 암호화되지 않은 IntPtr 버퍼에 액세스하는 방법을 알고 있다면 한 번에 한 바이트 씩 보안을 유지하기 위해 관리되는 문자열 개체를 만들 필요가 없습니다. 답을 추가하십시오. 🙂
static String SecureStringToString(SecureString value)
{
IntPtr bstr = Marshal.SecureStringToBSTR(value);
try
{
return Marshal.PtrToStringBSTR(bstr);
}
finally
{
Marshal.FreeBSTR(bstr);
}
}
답변
제 생각에는 확장 방법 이이 문제를 해결하는 가장 편안한 방법입니다.
나는 Steve를 CO의 훌륭한 대답으로 가져 와서 다음과 같이 확장 클래스에 넣었고 다른 방향 (문자열-> 보안 문자열)도 지원하기 위해 추가 한 두 번째 방법을 사용하여 보안 문자열을 만들고 다음으로 변환 할 수 있습니다. 나중에 일반 문자열 :
public static class Extensions
{
// convert a secure string into a normal plain text string
public static String ToPlainString(this System.Security.SecureString secureStr)
{
String plainStr=new System.Net.NetworkCredential(string.Empty, secureStr).Password;
return plainStr;
}
// convert a plain text string into a secure string
public static System.Security.SecureString ToSecureString(this String plainStr)
{
var secStr = new System.Security.SecureString(); secStr.Clear();
foreach (char c in plainStr.ToCharArray())
{
secStr.AppendChar(c);
}
return secStr;
}
}
이를 통해 이제 다음과 같이 문자열을 앞뒤로 간단히 변환 할 수 있습니다 .
// create a secure string
System.Security.SecureString securePassword = "MyCleverPwd123".ToSecureString();
// convert it back to plain text
String plainPassword = securePassword.ToPlainString(); // convert back to normal string
그러나 디코딩 방법은 테스트 용으로 만 사용해야합니다.
답변
메모리에서 해독 된 문자열을 더 잘 제어 할 수 있도록 SecureString
종속 함수가 익명 함수로 종속 논리 를 캡슐화 하는 것이 가장 좋습니다 (한 번 고정됨).
이 스 니펫에서 SecureStrings를 해독하기위한 구현은 다음과 같습니다.
- 메모리에 문자열을 고정하십시오 (원하는 일이지만 여기 대부분의 답변에서 누락 된 것처럼 보입니다).
- 패스 의 참조 Func을 / 액션 위양합니다.
- 메모리에서 스크럽하고
finally
블록 에서 GC를 해제합니다 .
이렇게하면 덜 바람직한 대안에 의존하는 것보다 호출자를 “표준화”하고 유지 관리하는 것이 훨씬 쉬워집니다.
string DecryptSecureString(...)
도우미 함수 에서 해독 된 문자열을 반환합니다 .- 필요한 곳에이 코드를 복제합니다.
여기에 두 가지 옵션이 있습니다.
static T DecryptSecureString<T>
이를 통해Func
호출자로부터 델리게이트 의 결과에 액세스 할 수 있습니다 (DecryptSecureStringWithFunc
테스트 메서드에 표시됨).static void DecryptSecureString
Action
실제로 아무것도 반환하지 않으려는 경우 (DecryptSecureStringWithAction
테스트 메서드 에서 설명한대로) 대리자 를 사용하는 “무효”버전입니다 .
둘 다에 대한 사용 예는 StringsTest
포함 된 클래스 에서 찾을 수 있습니다 .
Strings.cs
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace SecurityUtils
{
public partial class Strings
{
/// <summary>
/// Passes decrypted password String pinned in memory to Func delegate scrubbed on return.
/// </summary>
/// <typeparam name="T">Generic type returned by Func delegate</typeparam>
/// <param name="action">Func delegate which will receive the decrypted password pinned in memory as a String object</param>
/// <returns>Result of Func delegate</returns>
public static T DecryptSecureString<T>(SecureString secureString, Func<string, T> action)
{
var insecureStringPointer = IntPtr.Zero;
var insecureString = String.Empty;
var gcHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
try
{
insecureStringPointer = Marshal.SecureStringToGlobalAllocUnicode(secureString);
insecureString = Marshal.PtrToStringUni(insecureStringPointer);
return action(insecureString);
}
finally
{
//clear memory immediately - don't wait for garbage collector
fixed(char* ptr = insecureString )
{
for(int i = 0; i < insecureString.Length; i++)
{
ptr[i] = '\0';
}
}
insecureString = null;
gcHandler.Free();
Marshal.ZeroFreeGlobalAllocUnicode(insecureStringPointer);
}
}
/// <summary>
/// Runs DecryptSecureString with support for Action to leverage void return type
/// </summary>
/// <param name="secureString"></param>
/// <param name="action"></param>
public static void DecryptSecureString(SecureString secureString, Action<string> action)
{
DecryptSecureString<int>(secureString, (s) =>
{
action(s);
return 0;
});
}
}
}
StringsTest.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security;
namespace SecurityUtils.Test
{
[TestClass]
public class StringsTest
{
[TestMethod]
public void DecryptSecureStringWithFunc()
{
// Arrange
var secureString = new SecureString();
foreach (var c in "UserPassword123".ToCharArray())
secureString.AppendChar(c);
secureString.MakeReadOnly();
// Act
var result = Strings.DecryptSecureString<bool>(secureString, (password) =>
{
return password.Equals("UserPassword123");
});
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void DecryptSecureStringWithAction()
{
// Arrange
var secureString = new SecureString();
foreach (var c in "UserPassword123".ToCharArray())
secureString.AppendChar(c);
secureString.MakeReadOnly();
// Act
var result = false;
Strings.DecryptSecureString(secureString, (password) =>
{
result = password.Equals("UserPassword123");
});
// Assert
Assert.IsTrue(result);
}
}
}
분명히 이것은 다음과 같은 방식으로이 함수의 남용을 방지하지 못하므로 이렇게하지 않도록주의하십시오.
[TestMethod]
public void DecryptSecureStringWithAction()
{
// Arrange
var secureString = new SecureString();
foreach (var c in "UserPassword123".ToCharArray())
secureString.AppendChar(c);
secureString.MakeReadOnly();
// Act
string copyPassword = null;
Strings.DecryptSecureString(secureString, (password) =>
{
copyPassword = password; // Please don't do this!
});
// Assert
Assert.IsNull(copyPassword); // Fails
}
즐거운 코딩 되세요!
답변
rdev5 의 답변을 기반으로 다음 확장 방법을 만들었습니다 . 관리되는 문자열을 고정하는 것은 가비지 수집기가 문자열을 이동하고 지울 수없는 복사본을 남기는 것을 방지하기 때문에 중요합니다.
내 솔루션의 장점은 안전하지 않은 코드가 필요하지 않다는 것입니다.
/// <summary>
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
/// </summary>
/// <typeparam name="T">Generic type returned by Func delegate.</typeparam>
/// <param name="secureString">The string to decrypt.</param>
/// <param name="action">
/// Func delegate which will receive the decrypted password as a string object
/// </param>
/// <returns>Result of Func delegate</returns>
/// <remarks>
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
/// </remarks>
public static T UseDecryptedSecureString<T>(this SecureString secureString, Func<string, T> action)
{
int length = secureString.Length;
IntPtr sourceStringPointer = IntPtr.Zero;
// Create an empty string of the correct size and pin it so that the GC can't move it around.
string insecureString = new string('\0', length);
var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();
try
{
// Create an unmanaged copy of the secure string.
sourceStringPointer = Marshal.SecureStringToBSTR(secureString);
// Use the pointers to copy from the unmanaged to managed string.
for (int i = 0; i < secureString.Length; i++)
{
short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
}
return action(insecureString);
}
finally
{
// Zero the managed string so that the string is erased. Then unpin it to allow the
// GC to take over.
Marshal.Copy(new byte[length], 0, insecureStringPointer, length);
insecureStringHandler.Free();
// Zero and free the unmanaged string.
Marshal.ZeroFreeBSTR(sourceStringPointer);
}
}
/// <summary>
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
/// </summary>
/// <param name="secureString">The string to decrypt.</param>
/// <param name="action">
/// Func delegate which will receive the decrypted password as a string object
/// </param>
/// <returns>Result of Func delegate</returns>
/// <remarks>
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
/// </remarks>
public static void UseDecryptedSecureString(this SecureString secureString, Action<string> action)
{
UseDecryptedSecureString(secureString, (s) =>
{
action(s);
return 0;
});
}
답변
이 C # 코드는 원하는 것입니다.
%ProjectPath%/SecureStringsEasy.cs
using System;
using System.Security;
using System.Runtime.InteropServices;
namespace SecureStringsEasy
{
public static class MyExtensions
{
public static SecureString ToSecureString(string input)
{
SecureString secureString = new SecureString();
foreach (var item in input)
{
secureString.AppendChar(item);
}
return secureString;
}
public static string ToNormalString(SecureString input)
{
IntPtr strptr = Marshal.SecureStringToBSTR(input);
string normal = Marshal.PtrToStringBSTR(strptr);
Marshal.ZeroFreeBSTR(strptr);
return normal;
}
}
}