SecureString을 System.String로 변환하는 방법?
따로 System.String을 만들어 SecureString 보안을 해제하는 방법에 대한 모든 예약은 어떻게 할 수 있습니까?
일반 System.Security.SecureString을 System.String으로 변환하려면 어떻게해야합니까?
SecureString에 익숙한 많은 사람들이 모든 보안 보호 기능을 제거하므로 SecureString을 일반 .NET 문자열로 변환해서는 안된다고 응답 할 것입니다. 알고 있습니다. 그러나 지금 내 프로그램은 어쨌든 일반 문자열로 모든 작업을 수행하며 보안을 강화하려고 노력하고 있지만 보안 문자열을 반환하는 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);
}
}
메모리에서 해독 된 문자열을 한 번 더 잘 제어 SecureString
하기 위해 종속 함수가 종속 함수를 익명 함수 로 캡슐화 하는 것이 가장 좋습니다 .
이 스 니펫에서 SecureStrings를 해독하기위한 구현은 다음과 같습니다.
- 문자열을 메모리에 고정하십시오 (원하는 것이지만 대부분의 답변에서 누락 된 것처럼 보입니다).
- 패스 의 참조 Func을 / 액션 위양합니다.
- 메모리에서 제거하고
finally
블록 에서 GC를 해제하십시오 .
이렇게하면 덜 바람직한 대안에 의존하는 것보다 발신자를 "표준화"하고 유지하는 것이 훨씬 쉬워집니다.
string DecryptSecureString(...)
도우미 함수 에서 해독 된 문자열을 반환합니다 .- 필요할 때마다이 코드를 복제합니다.
여기에 두 가지 옵션이 있습니다.
static T DecryptSecureString<T>
테스트 메소드에Func
표시된대로 발신자 의 델리게이트 결과에 액세스 할 수 있습니다DecryptSecureStringWithFunc
.static void DecryptSecureString
는Action
실제로DecryptSecureStringWithAction
테스트 방법 에서 설명한대로 아무것도 반환하지 않으려는 경우 델리게이트 를 사용하는 "void"버전입니다 .
두 가지 사용법의 예는 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
}
행복한 코딩!
내 생각에 확장 방법 은이를 해결하는 가장 편안한 방법입니다.
나는 Steve를 CO의 훌륭한 대답으로 가져 와서 다음과 같이 확장 클래스에 넣었습니다. 다른 방향 (string-> secure string)을 지원하기 위해 추가 한 두 번째 방법과 함께 안전한 문자열을 만들고 변환 할 수 있습니다 이후 일반 문자열 :
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
그러나 디코딩 방법은 테스트 용도로만 사용해야합니다.
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;
}
}
}
위의 예를 사용하면 func 대리자 콜백 대신 내 경우가 더 분명해졌습니다. 물론 개발자가 처리해야합니다.
public class SecureStringContext : IDisposable
{
#region fields
private GCHandle? _gcHandler = null;
private string _insecureString = null;
private IntPtr? _insecureStringPointer = null;
private SecureString _secureString = null;
#endregion
#region ctor
public SecureStringContext(SecureString secureString)
{
_secureString = secureString;
_secureString.MakeReadOnly();
DecryptSecureString();
}
#endregion
#region methos
/// <summary>
/// Passes decrypted password String pinned in memory to Func delegate scrubbed on return.
/// </summary>
private string DecryptSecureString()
{
_insecureStringPointer = IntPtr.Zero;
_insecureString = String.Empty;
_gcHandler = GCHandle.Alloc(_insecureString, GCHandleType.Pinned);
_insecureStringPointer = Marshal.SecureStringToGlobalAllocUnicode(_secureString);
_insecureString = Marshal.PtrToStringUni(_insecureStringPointer.GetValueOrDefault(IntPtr.Zero));
return _insecureString;
}
private void WipeInsecureString()
{
//clear memory immediately - don't wait for garbage collector
unsafe
{
fixed (char* ptr = _insecureString)
{
for (int i = 0; i < _insecureString.Length; i++)
{
ptr[i] = '\0';
}
}
}
_insecureString = null;
}
#endregion
#region properties
public string InsecureString { get => _insecureString; }
#endregion
#region dispose
public void Dispose()
{
//clear memory immediately - don't wait for garbage collector
WipeInsecureString();
}
#endregion
}
사용법 (한 번 폐기 한 후에도 참고가됩니다.)
using (var secureStringContext = new SecureStringContext(FabricSettingsHelper.GetConnectionSecureString()))
{
//this is the clear text connection string
x.UseSqlServerStorage(secureStringContext.InsecureString);
} //disposed clear text is removed from memory
// using so that Marshal doesn't have to be qualified
using System.Runtime.InteropServices;
//using for SecureString
using System.Security;
public string DecodeSecureString (SecureString Convert)
{
//convert to IntPtr using Marshal
IntPtr cvttmpst = Marshal.SecureStringToBSTR(Convert);
//convert to string using Marshal
string cvtPlainPassword = Marshal.PtrToStringAuto(cvttmpst);
//return the now plain string
return cvtPlainPassword;
}
StringBuilder
대신에 를 사용하면 string
완료시 메모리의 실제 값을 덮어 쓸 수 있습니다. 이렇게하면 가비지 수집에서 암호를 찾을 때까지 암호가 메모리에 걸리지 않습니다.
StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
참고 URL : https://stackoverflow.com/questions/818704/how-to-convert-securestring-to-system-string
'programing tip' 카테고리의 다른 글
ASP.NET MVC에서 모델 상태 오류 수집을 어떻게 얻습니까? (0) | 2020.06.16 |
---|---|
더 큰 목록에서 모든 n 번째 항목의 목록을 반환하는 Pythonic 방법 (0) | 2020.06.16 |
C에서 "콜백"이란 무엇이며 어떻게 구현됩니까? (0) | 2020.06.16 |
Objective-C에서 NSString 토큰 화 (0) | 2020.06.16 |
가운데 맞춤을 어떻게 중앙 정렬 (0) | 2020.06.15 |