SecureString在C#应用程序中实用吗?
如果我的假设是错误的,请随时纠正我,但让我解释一下为什么我会这样问 取自MSDN,一个SecureString在C#应用程序中实用吗?,c#,security,C#,Security,如果我的假设是错误的,请随时纠正我,但让我解释一下为什么我会这样问 取自MSDN,一个SecureString: 表示应保密的文本。文本在使用时为了隐私而加密,在不再需要时从计算机内存中删除 我明白了,通过System.String,将密码或其他私人信息存储在SecureString中是完全有意义的,因为您可以控制它实际存储在内存中的方式和时间,因为System.String: 是不可变的,并且当不再需要时,不能以编程方式安排垃圾收集;也就是说,实例创建后是只读的,无法预测何时从计算机内存中删除
SecureString
:
表示应保密的文本。文本在使用时为了隐私而加密,在不再需要时从计算机内存中删除
我明白了,通过System.String
,将密码或其他私人信息存储在SecureString
中是完全有意义的,因为您可以控制它实际存储在内存中的方式和时间,因为System.String
:
是不可变的,并且当不再需要时,不能以编程方式安排垃圾收集;也就是说,实例创建后是只读的,无法预测何时从计算机内存中删除实例。因此,如果字符串对象包含敏感信息,如密码、信用卡号或个人数据,则在使用该信息后可能会泄露该信息,因为您的应用程序无法从计算机内存中删除该数据
但是,对于GUI应用程序(例如,ssh客户端),SecureString
必须从System.String
构建。所有文本控件都使用字符串作为其基础数据类型
因此,这意味着每次用户按下一个键时,原有的字符串都会被丢弃,并生成一个新字符串来表示文本框中的值,即使使用密码掩码也是如此。我们无法控制这些值何时或是否从内存中被丢弃
现在是登录服务器的时候了。你猜怎么着?您需要通过连接传递字符串以进行身份验证。因此,让我们将SecureString
转换为System.String
。。。。现在堆上有一个字符串,无法强制它进行垃圾收集(或将0写入其缓冲区)
我的观点是:不管你做什么,在这条路线上的某个地方,SecureString
将被转换成一个System.String
,这意味着它至少会在某个点存在于堆上(不保证垃圾收集)
我的观点不是:是否有办法避免向ssh连接发送字符串,或者避免让控件存储字符串(创建自定义控件)。对于这个问题,您可以将“ssh连接”替换为“登录表单”、“注册表单”、“付款表单”、“您将喂养您的小狗但不喂养您的孩子的食品表单”等
- 那么,在什么情况下使用
才是真正的问题呢 实用的SecureString
- 是否值得花费额外的开发时间来彻底根除病毒
对象的使用System.String
的全部目的是为了减少SecureString
在堆上的时间(降低其移动到物理交换文件的风险)系统.String
- 如果攻击者已经有了堆检查的方法,那么他很可能(a)已经有了读取击键的方法,或者(B)已经实际拥有了机器。。。那么,使用
会阻止他以任何方式获取数据吗SecureString
- 这仅仅是“默默无闻的安全”吗
对不起,如果我把问题说得太过分了,好奇心就占了上风。请随意回答我的任何或所有问题(或告诉我我的假设完全错误):) 在您的假设中,这些问题很少 首先,SecureString类没有字符串构造函数。为了创建一个对象,需要分配一个对象,然后附加字符 对于GUI或控制台,您可以非常轻松地将每个按下的键传递给一个安全字符串 该类的设计方式使您不能错误地访问存储的值。这意味着您不能直接从中获取
字符串作为密码
因此,为了使用它,例如,通过web进行身份验证,您必须使用适当的安全类
在.NET framework中,有几个类可以使用SecureString
- WPF的PasswordBox控件在内部将密码保持为SecureString
- System.Diagnostics.ProcessInfo的密码属性是SecureString
- X509Certificate2的构造函数采用SecureString作为密码李>
总之,SecureString类可能很有用,但需要开发人员给予更多关注
所有这些都在MSDN的文档中有很好的描述,并附有示例。如果满足以下条件,SecureString将非常有用:
- 您可以逐个字符构建它(例如,从控制台输入)或从非托管API获取它
- 您可以通过将其传递给非托管API(SecureStringToBSTR)来使用它
如果您曾经将其转换为托管字符串,那么您就破坏了它的用途
更新以回应评论
。。。或者像你提到的BSTR,看起来不太安全
将其转换为BSTR后,使用BSTR的非托管组件可以将内存归零。非托管内存更安全,因为它可以通过这种方式重置
然而,.NET Framework中支持SecureString的API非常少,因此您可以说它在今天的价值非常有限
我看到的主要用例是在客户端应用程序中,它要求用户输入高度敏感的代码或密码。用户输入可以逐个字符地用于构建SecureString,然后可以将其传递给非托管API,该API在使用BSTR后将其接收的BSTR归零。任何后续内存转储都不会包含敏感字符串
在服务器应用程序中,很难看到它将在何处使用
// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
//...snip...
}
finally
{
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}
namespace Cardinity.Infrastructure
{
using System.Security.Cryptography;
using System;
enum EncryptionMethods
{
None=0,
HMACSHA1,
HMACSHA256,
HMACSHA384,
HMACSHA512,
HMACMD5
}
internal class Protected
{
private Byte[] salt = Guid.NewGuid().ToByteArray();
protected byte[] Protect(byte[] data)
{
try
{
return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
}
catch (CryptographicException)//no reason for hackers to know it failed
{
#if DEBUG
throw;
#else
return null;
#endif
}
}
protected byte[] Unprotect(byte[] data)
{
try
{
return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
}
catch (CryptographicException)//no reason for hackers to know it failed
{
#if DEBUG
throw;
#else
return null;
#endif
}
}
}
internal class SecretKeySpec:Protected,IDisposable
{
readonly EncryptionMethods _method;
private byte[] _secretKey;
public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
{
_secretKey = Protect(secretKey);
_method = encryptionMethod;
}
public EncryptionMethods Method => _method;
public byte[] SecretKey => Unprotect( _secretKey);
public void Dispose()
{
if (_secretKey == null)
return;
//overwrite array memory
for (int i = 0; i < _secretKey.Length; i++)
{
_secretKey[i] = 0;
}
//set-null
_secretKey = null;
}
~SecretKeySpec()
{
Dispose();
}
}
internal class Mac : Protected,IDisposable
{
byte[] rawHmac;
HMAC mac;
public Mac(SecretKeySpec key, string data)
{
switch (key.Method)
{
case EncryptionMethods.HMACMD5:
mac = new HMACMD5(key.SecretKey);
break;
case EncryptionMethods.HMACSHA512:
mac = new HMACSHA512(key.SecretKey);
break;
case EncryptionMethods.HMACSHA384:
mac = new HMACSHA384(key.SecretKey);
break;
case EncryptionMethods.HMACSHA256:
mac = new HMACSHA256(key.SecretKey);
break;
case EncryptionMethods.HMACSHA1:
mac = new HMACSHA1(key.SecretKey);
break;
default:
throw new NotSupportedException("not supported HMAC");
}
rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));
}
public string AsBase64()
{
return System.Convert.ToBase64String(Unprotect(rawHmac));
}
public void Dispose()
{
if (rawHmac != null)
{
//overwrite memory address
for (int i = 0; i < rawHmac.Length; i++)
{
rawHmac[i] = 0;
}
//release memory now
rawHmac = null;
}
mac?.Dispose();
mac = null;
}
~Mac()
{
Dispose();
}
}
}