【发布时间】:2010-12-06 22:12:40
【问题描述】:
如何将String 转换为SecureString?
【问题讨论】:
标签: c# .net security securestring
如何将String 转换为SecureString?
【问题讨论】:
标签: c# .net security securestring
我会把它扔出去。为什么?
您不能只是将所有字符串更改为安全字符串,然后您的应用程序突然就“安全”了。安全字符串旨在尽可能长时间地保持字符串加密,并且仅在很短的时间内解密,在对其执行操作后擦除内存。
在担心保护您的应用程序字符串之前,我会冒昧地说您可能需要处理一些设计级别的问题。向我们提供更多关于您尝试做什么的信息,我们可能会提供更好的帮助。
【讨论】:
你没有。使用 SecureString 对象的全部原因是避免创建字符串对象(该对象被加载到内存中并以纯文本形式保存在那里,直到垃圾回收)。但是,您可以通过附加字符将字符添加到 SecureString。
var s = new SecureString();
s.AppendChar('d');
s.AppendChar('u');
s.AppendChar('m');
s.AppendChar('b');
s.AppendChar('p');
s.AppendChar('a');
s.AppendChar('s');
s.AppendChar('s');
s.AppendChar('w');
s.AppendChar('d');
【讨论】:
12345...这就是白痴在他的行李箱上的那种东西!”
*****
AppendChar 直到运行时才会执行,所以这些字符仍然可以从 IL 中读取,对吗?也许一些编译时混淆也会有所帮助......也就是说,如果你在硬编码密码。
我同意 Spence (+1),但如果您这样做是为了学习或测试 pourpose,您可以在字符串中使用 foreach,使用 AppendChar 方法将每个字符附加到安全字符串。
【讨论】:
以下方法有助于将字符串转换为安全字符串
private SecureString ConvertToSecureString(string password)
{
if (password == null)
throw new ArgumentNullException("password");
var securePassword = new SecureString();
foreach (char c in password)
securePassword.AppendChar(c);
securePassword.MakeReadOnly();
return securePassword;
}
【讨论】:
你可以按照这个:
string password = "test";
SecureString sec_pass = new SecureString();
Array.ForEach(password.ToArray(), sec_pass.AppendChar);
sec_pass.MakeReadOnly();
【讨论】:
unsafe
{
fixed(char* psz = password)
return new SecureString(psz, password.Length);
}
【讨论】:
这是一个廉价的 linq 技巧。
SecureString sec = new SecureString();
string pwd = "abc123"; /* Not Secure! */
pwd.ToCharArray().ToList().ForEach(sec.AppendChar);
/* and now : seal the deal */
sec.MakeReadOnly();
【讨论】:
如果您想将string 到SecureString 的转换压缩成LINQ 语句,可以如下表示:
var plain = "The quick brown fox jumps over the lazy dog";
var secure = plain
.ToCharArray()
.Aggregate( new SecureString()
, (s, c) => { s.AppendChar(c); return s; }
, (s) => { s.MakeReadOnly(); return s; }
);
但是,请记住,使用 LINQ 并不能提高此解决方案的安全性。它存在与从string 到SecureString 的任何转换相同的缺陷。只要原始的string 保留在内存中,数据就很容易受到攻击。
话虽如此,上述语句可以提供的是将SecureString 的创建、使用数据初始化并最终锁定它以防修改。
【讨论】:
没有花哨的 linq,没有手动添加所有字符,简单明了:
var str = "foo";
var sc = new SecureString();
foreach(char c in str) sc.appendChar(c);
【讨论】:
var sc = new SecureString(); foreach(char c in "foo") sc.appendChar(c);
还有另一种方法可以在SecureString 和String 之间进行转换。
1.字符串到 SecureString
SecureString theSecureString = new NetworkCredential("", "myPass").SecurePassword;
2。安全字符串到字符串
string theString = new NetworkCredential("", theSecureString).Password;
这里是link
【讨论】:
string 对象,那么使用SecureString 是没有意义的。 SecureString 的目标是避免将字符串放在托管内存中,因此检查该内存的攻击者可以发现您想要隐藏的值。由于您正在调用的NetworkCredential 构造函数需要一个字符串,这不是要走的路...当然,您的代码是与 SecureString 相互转换的简单方法,但是使用它可以使您的代码像使用简单的string
SecureString。老实说,如果有人正在积极扫描您的应用程序的内存,那么您已经迷路了。即使你正确使用了我从未见过的 SecureString,也会有其他有价值的数据需要提取。
以下 2 个扩展应该可以解决问题:
对于char 数组
public static SecureString ToSecureString(this char[] _self)
{
SecureString knox = new SecureString();
foreach (char c in _self)
{
knox.AppendChar(c);
}
return knox;
}
对于string
public static SecureString ToSecureString(this string _self)
{
SecureString knox = new SecureString();
char[] chars = _self.ToCharArray();
foreach (char c in chars)
{
knox.AppendChar(c);
}
return knox;
}
感谢John Dagg 的AppendChar 推荐。
【讨论】:
你可以使用这个简单的脚本
private SecureString SecureStringConverter(string pass)
{
SecureString ret = new SecureString();
foreach (char chr in pass.ToCharArray())
ret.AppendChar(chr);
return ret;
}
【讨论】:
我只想向所有人指出,“这不是 SecureString 的意义所在”,许多提出这个问题的人可能在一个应用程序中,无论出于何种原因,无论是否合理,他们不特别担心将密码的临时副本作为可 GC 的字符串放在堆上,但他们必须使用 仅 接受 SecureString 的 API对象。所以,你有一个应用程序,你不关心密码是否在堆上,也许它仅供内部使用,密码只是因为底层网络协议需要它,而你发现存储密码的字符串不能用于例如设置远程 PowerShell 运行空间——但没有简单、直接的单行代码来创建您需要的 SecureString。这是一个小小的不便——但可能值得确保真正确实需要SecureString 的应用程序不会诱使作者使用System.String 或System.Char[] 中介。 :-)
【讨论】:
为了完整起见,我添加了两个单元测试和方法来从 char 数组和字符串转换为 SecureString 并再次转换回来。您应该尝试完全避免使用字符串,并且只传递指向 char 数组的指针或 char 数组本身,就像我在此处提供的方法一样,因为字符串是不安全的,因为它们将数据以纯文本形式保存在托管内存中,数量相当不确定距离下一次 GC 运行或强制执行的时间,最好尽快将您的字符数组放入 SecureString 并保留在那里,并再次将其作为字符数组读回。
测试如下所示:
using NUnit.Framework;
using System;
using SecureStringExtensions;
using System.Security;
namespace SecureStringExtensions.Test
{
[TestFixture]
public class SecureStringExtensionsTest
{
[Test]
[TestCase(new char[] { 'G', 'O', 'A', 'T', '1', '2', '3' })]
public void CopyCharArrayToSecureStringAndCopyBackToCharArrayReturnsExpected(char[] inputChars)
{
SecureString sec = inputChars.ToSecureString();
var copiedFromSec = sec.FromSecureStringToCharArray();
CollectionAssert.AreEqual(copiedFromSec, inputChars);
}
[Test]
[TestCase("GOAT456")]
public void CopyStringToSecureStringAndCopyBackToUnsafeStringReturnsExpected(string inputString)
{
SecureString sec = inputString.ToSecureString();
var copiedFromSec = sec.FromSecureStringToUnsafeString();
Assert.AreEqual(copiedFromSec, inputString);
}
}
}
我们在这里有我们的扩展方法:
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace SecureStringExtensions
{
public static class SecureStringExtensions
{
public static SecureString ToSecureString(this string str)
{
return ToSecureString(str.ToCharArray());
}
public static SecureString ToSecureString(this char[] str)
{
var secureString = new SecureString();
Array.ForEach(str, secureString.AppendChar);
return secureString;
}
/// <summary>
/// Creates a managed character array from the secure string using methods in System.Runetime.InteropServices
/// copying data into a BSTR (unmanaged binary string) and then into a managed character array which is returned from this method.
/// Data in the unmanaged memory temporarily used are freed up before the method returns.
/// </summary>
/// <param name="secureString"></param>
/// <returns></returns>
public static char[] FromSecureStringToCharArray(this SecureString secureString)
{
char[] bytes;
var ptr = IntPtr.Zero;
try
{
//alloc unmanaged binary string (BSTR) and copy contents of SecureString into this BSTR
ptr = Marshal.SecureStringToBSTR(secureString);
bytes = new char[secureString.Length];
//copy to managed memory char array from unmanaged memory
Marshal.Copy(ptr, bytes, 0, secureString.Length);
}
finally
{
if (ptr != IntPtr.Zero)
{
//free unmanaged memory
Marshal.ZeroFreeBSTR(ptr);
}
}
return bytes;
}
/// <summary>
/// Returns an unsafe string in managed memory from SecureString.
/// The use of this method is not recommended - use instead the <see cref="FromSecureStringToCharArray(SecureString)"/> method
/// as that method has not got multiple copies of data in managed memory like this method.
/// Data in unmanaged memory temporarily used are freed up before the method returns.
/// </summary>
/// <param name="secureString"></param>
/// <returns></returns>
public static string FromSecureStringToUnsafeString(this SecureString secureString)
{
if (secureString == null)
{
throw new ArgumentNullException(nameof(secureString));
}
var unmanagedString = IntPtr.Zero;
try
{
//copy secure string into unmanaged memory
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
//alloc managed string and copy contents of unmanaged string data into it
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
if (unmanagedString != IntPtr.Zero)
{
Marshal.FreeBSTR(unmanagedString);
}
}
}
}
}
在这里,我们使用 System.Runtime.InteropServices 中的方法来临时分配 BSTR(二进制非托管字符串),并确保释放临时使用的非托管内存。
【讨论】: