如果我的假设在这里错了,请随意纠正我,但让我解释一下我为什么要问.
摘自MSDN,a SecureString:
代表应保密的文本.文本在使用时加密以保护隐私,并在不再需要时从计算机内存中删除.
我明白了,将密码或其他私人信息存储在a中是完全合理SecureString的System.String,因为你可以控制它实际存储在内存中的方式和时间,因为System.String:
是不可变的,当不再需要时,不能以编程方式安排垃圾收集; 也就是说,实例在创建后是只读的,并且无法预测实例何时从计算机内存中删除.因此,如果String对象包含敏感信息(如密码,信用卡号或个人数据),则使用该信息后可能会显示该信息,因为您的应用程序无法从计算机内存中删除该数据.
但是,在GUI应用程序(例如,ssh客户端)的SecureString 情况下,必须从a构建 System.String.所有文本控件都使用字符串作为其基础数据类型.
因此,这意味着每次用户按下某个键时,就会丢弃那里的旧字符串,并构建一个新字符串来表示文本框内的值,即使使用密码掩码.并且我们无法控制何时或是否从内存中丢弃任何这些值.
现在是时候登录服务器了.你猜怎么着?您需要在连接上传递一个字符串以进行身份验证.所以让我们将我们SecureString转换为System.String....现在我们在堆上有一个字符串,无法强制它通过垃圾收集(或将0写入其缓冲区).
我的观点是:无论你做什么,沿线某处,那SecureString是会被转换成一个System.String,这意味着它至少会在某些点上堆存在(没有垃圾收集的任何保证).
我的观点不是:是否存在绕过将字符串发送到ssh连接或绕过控件存储字符串(制作自定义控件)的方法.对于这个问题,您可以将"ssh connection"替换为"登录表单","注册表单","付款表单","食物 - 你愿意喂你的小狗但不是你孩子的表格",等等
SecureString实际变得实用?System.String对象的使用?SecureString减少System.String堆上的时间(降低转移到物理交换文件的风险)?SecureString阻止他进入数据呢?对不起,如果我把问题放得太厚,好奇心就好了.随意回答我的任何或所有问题(或告诉我,我的假设是完全错误的).:)
我有一个带有两个PasswordBox的WPF应用程序,一个用于密码,另一个用于第二次输入密码以进行确认.我想用来PasswordBox.SecurePassword获取SecureString密码,但我需要能够比较两个PasswordBoxes的内容以确保在我接受密码之前的相等性.但是,两个相同的SecureStrings不相同:
var secString1 = new SecureString();
var secString2 = new SecureString();
foreach (char c in "testing")
{
secString1.AppendChar(c);
secString2.AppendChar(c);
}
Assert.AreEqual(secString1, secString2); // This fails
Run Code Online (Sandbox Code Playgroud)
我在想比较PasswordPasswordBoxes 的属性会破坏访问点,SecurePassword因为我正在阅读纯文本密码.如何在不牺牲安全性的情况下比较两个密码?
编辑:根据这个问题,我正在查看这篇关于"使用Marshal类将SecureString转换为ANSI或Unicode或BSTR"的博文,然后我可以比较一下.
有没有办法在不包含安全性的情况下获取SecureString的值?例如,在下面的代码中,只要执行PtrToStringBSTR,字符串就不再安全,因为字符串是不可变的,垃圾收集对于字符串是不确定的.
IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object);
string value = Marshal.PtrToStringBSTR(ptr);
Run Code Online (Sandbox Code Playgroud)
如果有一种方法可以获得非托管BSTR字符串的char []或byte [],该怎么办?这是否意味着垃圾收集更可预测(因为你将使用char []或byte []而不是字符串?这个假设是正确的,如果是这样,你将如何得到char []或byte []?
我想启动一个具有管理员权限的进程.当我运行以下代码时,Process抱怨说它需要管理员权限:
public class ImpersonationHelper : IDisposable
{
IntPtr m_tokenHandle = new IntPtr(0);
WindowsImpersonationContext m_impersonatedUser;
#region Win32 API Declarations
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2; //This parameter causes LogonUser to create a primary token.
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr …Run Code Online (Sandbox Code Playgroud) 所以这个类似乎很少使用:SecureString.它至少从2.0开始就存在,并且有一些SO问题,但我想我会问自己的具体问题:
我有一个LoginForm; 带有用户名和(掩码)密码字段的简单WinForms对话框.当用户同时输入并单击"登录"时,信息将传递到执行一层键扩展的注入认证类,然后散列一半拉伸键进行验证,而另一半是加密用户的对称密钥帐户数据.完成所有这些操作后,将关闭loginForm,处理身份验证器类,然后系统继续加载主窗体.非常标准的东西,可能比标准的哈希 - 密码和比较更多一些,但是在我的情况下,简单的哈希密码将通过以明文形式存储用户数据而被打败,因为该数据包括第三个的凭证 - 派对系统(我们都知道人们如何重用密码).
这是第一个问题; 我如何使用SecureString从密码文本框中检索密码,而不是通过文本框的Text属性将其公开为普通的System.String?我假设有一种方法可以访问由CLR类包装的Textbox的非托管GDI窗口,并使用Marshal类提取文本数据.我只是不知道怎么样,而且我似乎无法找到好的信息.
这是第二个问题; 一旦我将密码作为SecureString,我如何从System.Security.Crypto命名空间将其传递给哈希提供程序?我的猜测是我使用Marshal.SecureStringToBSTR(),然后Marshal.Copy()从返回的IntPtr返回到一个字节数组.然后我可以调用Marshal.ZeroBSTR()来清理非托管内存,一旦我有哈希,我可以用Array.Clear()将托管数组清零.如果有一种更简洁的方法可以让我完全控制内存的任何托管副本的生命周期,请告诉我们.
第三个问题; 这一切真的是必要的,还是在托管内存环境中System.String固有的不安全性有点过分了?在操作系统考虑将应用程序交换到虚拟内存之前很久,任何用于存储密码(加密或其他方式)的内容都应该超出范围并且在前往垃圾收集器的路上(允许在交换文件后从交换文件中嗅探密码)硬关机的电脑).冷启动攻击是一种理论上的可能性,但实际上,这有多常见?更大的问题是现在解密的用户数据,它在整个应用程序生命周期中作为用户的一部分而挂起(因此将成为使用SecureStrings的主要候选者,因为除了几个基本用法之外,它们保持相当休眠状态).
因此,.NET框架提供了SecureString类,用于以安全的方式存储字符串.但要阅读信息并使用它,您必须将其返回到标准字符串.请参阅此实现示例.
正如您在使用指针的示例中看到的,我们返回一个未加密的字符串.我们现在如何管理字符串的"不安全"实例?一旦设置了值,最安全的方法是什么?
编辑
这个问题的目的是讨论在使用SecureStrings然后使用这些值时减少潜在攻击的表面区域的方法.关于"重复"链接不是"为什么".
在学习Java时,我了解到Strings 对于存储密码是不安全的,因为你无法手动清除与它们相关的内存(你不能确定它们最终会被gc'ed,内部字符串可能永远不会,甚至之后gc你不能确定物理内存内容真的被擦除了).相反,我使用char数组,所以我可以在使用后将它们清零.我试图在其他语言和平台上搜索类似的做法,但到目前为止我找不到相关的信息(通常我看到的是密码存储在字符串中的代码示例,没有提到任何安全问题).
我对浏览器的情况特别感兴趣.我经常使用jQuery,而我通常的做法就是将密码字段的值设置为空字符串而忘记它:
$(myPasswordField).val("");
Run Code Online (Sandbox Code Playgroud)
但我并不是100%确信它已经足够了.我也不知道用于中间访问的字符串是否安全(例如,当我$.ajax用来将密码发送到服务器时).至于其他语言,通常我没有提到这个问题(我特别感兴趣的另一种语言是Python).
我知道试图建立列表的问题是有争议的,但由于这涉及一个很大程度上被忽视的共同安全问题,恕我直言,这是值得的.如果我弄错了,我会很高兴知道从JavaScript(在浏览器中)和Python.我也不确定是在这里,在security.SE还是在程序员.SE,但由于它涉及安全执行任务的实际代码(不是概念性问题),我相信这个网站是最好的选择.
注意:在低级语言或明确支持字符作为原始类型的语言中,答案应该是显而易见的(编辑:不是很明显,正如@Gabe在下面的答案中所示).我要求那些"一切都是对象"或类似的高级语言,以及那些在幕后执行自动字符串实习的人(所以你可能会创建一个安全漏洞而不会意识到它,即使你'合理谨慎).
更新:根据相关问题的答案,即使char[]在Java中使用也不能保证是防弹的(或者就此而言是.NET SecureString),因为gc可能会移动数组,因此其内容可能会粘在内存中,即使在清除(SecureString至少保留在相同的RAM地址中,保证清除,但其消费者/生产者可能仍然留下痕迹).
我猜@NiklasB.是正确的,即使漏洞存在,漏洞利用的可能性很低,防止漏洞的难度很高,这可能是这个问题大多被忽视的原因.我希望我至少可以找到关于浏览器这个问题的一些参考,但谷歌搜索到目前为止一直没有结果(这个场景至少有一个名字吗?).
我有一个方法当前返回从字节数组转换而来的字符串:
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
if (string.IsNullOrEmpty(textToDecrypt))
{
throw new ArgumentException(
"Cannot decrypt null or blank string"
);
}
if (string.IsNullOrEmpty(privateKeyXml))
{
throw new ArgumentException("Invalid private key XML given");
}
byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt);
byte[] decryptedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKeyXml);
decryptedBytes = rsa.Decrypt(bytesToDecrypt, FOAEP);
}
return ByteConverter.GetString(decryptedBytes);
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试更新此方法以返回 a ,但在将from的返回值转换为 时SecureString遇到问题。我尝试了以下方法:RSACryptoServiceProvider.Decryptbyte[]SecureString
var secStr = new SecureString();
foreach (byte …Run Code Online (Sandbox Code Playgroud) 在proxy.exe中,我通过以下方式创建安全字符串:
public SecureString GetSecureEncryptionKey()
{
string strPassword = "8charPwd";
SecureString secureStr = new SecureString();
if (strPassword.Length > 0)
{
foreach (var c in strPassword.ToCharArray()) secureStr.AppendChar(c);
}
return secureStr;
}
Run Code Online (Sandbox Code Playgroud)
然后在main.exe中我使用此函数解密它:
public string convertToUNSecureString(SecureString secstrPassword)
{
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secstrPassword);
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
Run Code Online (Sandbox Code Playgroud)
问题是返回的字符串是空的,除非我加密main.exe中的初始字符串,然后返回的解密字符串确实是"8charPwd".为什么会这样?SecureString加密是否绑定到可执行文件?
非常密切相关:如何在没有 SecureString 的情况下保护字符串?
也密切相关:我什么时候需要 .NET 中的 SecureString?
极其密切相关(OP 正在尝试实现非常相似的东西):C# & WPF - Using SecureString for a client-side HTTP API password
.NET Framework 有一个名为SecureString 的类。但是,即使是 Microsoft 也不再建议将其用于新的开发。根据第一个链接的问答,至少有一个原因是该字符串无论如何都会以明文形式存在于内存中至少一段时间(即使时间很短)。至少有一个答案还扩展了一个论点,即如果他们无论如何都可以访问服务器的内存,那么实际上安全性无论如何都可能会被击中,所以它不会帮助你。(第二个链接的问答暗示甚至讨论过将其从 .NET Core 中完全删除)。
话虽如此,微软的文档SecureString不建议更换,并且关于链接问答的共识似乎是这种措施无论如何都不会那么有用。
我的应用程序是一个 ASP.NET Core 应用程序,它广泛使用了对使用HttpClient该类的外部供应商的 API 调用。在通常推荐的最佳做法的HttpClient是使用单个实例,而不是每个调用创建一个新的实例。
但是,我们的供应商要求所有 API 调用都包含我们的 API 密钥作为具有特定名称的标头。我目前安全地存储密钥,在 中检索它Startup.cs,并将其添加到我们HttpClient实例的标头中。
不幸的是,这意味着我的 API 密钥将在应用程序的整个生命周期中以明文形式保存在内存中。我发现这对于服务器上的 Web 应用程序来说尤其令人不安;尽管服务器由企业 IT 维护,但我一直被教导将企业网络视为半敌对环境,在这种情况下,不要纯粹依赖企业防火墙来确保应用程序安全。
对于此类情况,Microsoft 是否有推荐的最佳实践?这是他们反对使用的建议的潜在例外SecureString吗?(具体如何工作是一个单独的问题)。或者其他问答中的答案是否真的正确说我不应该担心像这样存在于内存中的明文字符串?
注意:根据对这个问题的回答,我可能会发布一个关于是否可以将类似的内容SecureString用作HttpClient标题的一部分的后续问题。或者我是否必须做一些棘手的事情,例如在使用之前填充标题,然后立即将其从内存中删除?(虽然这会给并发调用带来绝对的噩梦)。如果人们认为我应该做这样的事情,我很乐意为此创建一个新问题。
security encryption dotnet-httpclient .net-core asp.net-core
c# ×7
securestring ×6
security ×4
.net ×2
encryption ×2
passwords ×2
.net-core ×1
arrays ×1
asp.net-core ×1
equals ×1
passwordbox ×1
rsa ×1
string ×1
wcf ×1