【发布时间】:2015-03-14 11:04:44
【问题描述】:
我正在尝试保护我的 Windows 服务项目免受内存刮板的影响。我正在尝试存储一些极其敏感的数据。让我们以信用卡号“1234-5678-1234-5678”为例。我需要能够将这些数据存储到两个主要的 C# 对象中,并在完成后将其删除。
我已经能够在 StackOverflow 示例的帮助下构建的自定义类中存储和删除敏感数据:
public void DestorySensitiveData()
{
try
{
unsafe
{
int iDataSize = m_chSensitiveData.Length;
byte[] clear = new byte[iDataSize];
for (int i = 0; i < clear.Length; i++)
{
clear[i] = 70; //fixed ascii character
}
fixed (char* ptr = &m_chSensitiveData[0])
{
System.Runtime.InteropServices.Marshal.Copy(clear, 0, new IntPtr(ptr), iDataSize);
}
}
}
catch (Exception e)
{
}
}
通过使用字符数组而不是字符串,我能够覆盖/擦除内存中的敏感数据。如果没有这个类,.NET 和字符串的内存管理可以并将这些数据复制到任何需要它的地方。即使我的课程超出范围或我试图调用 dispose,我也可以运行内存刮擦并以纯文本形式找到我的敏感数据,就像您阅读本段一样容易。
我正在寻找 Streams 的实践或方法。我需要能够将敏感数据移入/移出 System.Diagnostics.Process() 甚至是文件。当我使用它时,数据以纯文本形式显示是“可以的”——当我用完它时,它就不能在内存中的任何地方保留。这包括由内存管理或垃圾收集制作的副本。
不起作用的示例:
- Strings/StringBuilders:它们在不断更新自己,到处都是碎片。
- SecureString:类在纸面上和 MSDN 上看起来都很棒。但是您必须使用字符串来加载和卸载此对象(请参阅第 1 项)
- 数组列表。真的是二维的字符串问题。
我什至尝试创建一个单独的 EXE 项目并在该进程中执行我对内存敏感的工作。当我不得不重载输入/输出流时,我有点吃不消了。
流似乎被复制到各处。在我的应用程序中,我创建了一个流,将敏感数据加载到其中然后完成。我加载了我的敏感数据一次,执行完成后,我在原始内存中发现了大约十几个完整的副本。
以前有没有人在 .NET 中遇到过类似的问题?他们是如何解决的?
更新:2015 年 1 月 16 日:
我必须通过在同一台机器上运行的软件发送信用卡进行处理:
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.WorkingDirectory = @"<full working path>";
proc.StartInfo.FileName = @"<vendor EXE>";
proc.StartInfo.Arguments = "<args>";
proc.Start();
//pTran.ProcessorData.ProcID = proc.Id;
StreamWriter myWriter = proc.StandardInput;
StreamReader myReader = proc.StandardOutput;
// Send request API to Protobase
//I need a process to send the data
//even if I use a protected object to hold it,
//the 'myWriter' cannot be controlled
**myWriter.Write(sSensitiveData);**
myWriter.Flush();
myWriter.Close();
// Read Response API
string sResponseData = myReader.ReadToEnd();
Console.Write(sResponseData);
proc.WaitForExit();
proc.Close();
myReader.Close();
这就是我询问 Stream 类并破坏它们包含的内存的原因。与我们作为哈希存储和比较的密码不同,此数据必须由我们的供应商读取。
@Greg:我喜欢你在我的流程和供应商之间相互同意加密的想法。我已经在研究这个角度了。
除了数据加密和允许 GC 到处复制片段之外,有没有办法从 Stream 类型的类中清理内存?
【问题讨论】:
-
您是否考虑过对值进行强加密并在目标或源计算机上编写或使用一些解密代码..?
-
您能否更具体地说明为什么 SecureString 不能满足您的需求?
-
是的! SecureString 对我不起作用的原因是我必须将此信息发送给第 3 方供应商,然后阅读响应。我正在创建一个 System.Diagnostics.Process() 并重载输入/输出以传递数据。我知道的唯一可以做到这一点的对象是 Stream/StreamWriter 类。我还有一个 File I/0 选项(需要调用 sdelete.exe),但 C# 中的 File I/O 也不能使用 SecureString。
-
读入一个预分配的字节数组。然后将该值转换为 SecureString 并擦除字节数组。此时无法从内存中读取。当你需要做 I/O 时,做相反的事情。将 SecureString 中的值放入字节数组中,传输并再次擦除。当然,在传输过程中,它位于内存和 I/O 通道上,因此可以从那里读取。理论上,至少。
-
一个建议 - 当您创建
m_chSensitiveData对象时,请务必使用GCHandle.Alloc(m_chSensitiveData, GCHandleType.Pinned)将其固定,并将句柄存储为成员,然后在清除后在句柄上调用.Free()。如果/当堆被压缩时,这将防止您的对象被移动,因为移动它将留下位直到被覆盖。fixed已经在其范围内执行此操作,但您可能不希望它在对象的生命周期内移动。但是,我很惊讶 Array.Clear() 对于 char 数组(值类型)来说还不够好。
标签: c# string security stream garbage-collection