从具体上来说,.NET元数据机制的设计,既方便了反射等强大特性的实现,又同时给代码安全及程序运行时安全带来了巨大的隐患。迄今为止,还未发现比较有效元数据可见性控制方法。当然,这不在本文的讨论范围之内。我还是更愿意在这篇文章在针对.NET的内存分配机制讨论一个更具体的问题:如何保护在内存中存储的敏感数据?
String的驻留机制带来的安全性问题
String是代码中使用频率很高的对象类型。为了提高字符串的处理速度,节省内存空间,Microsoft为.NET String类设计了驻留机制。其大概的逻辑模型是,大部分String存储在一个类似的Hash Table中,string的内容是哈希表的key,该key对应的value是string的内存地址。这样内容相同的string实际上只是对应内存堆上同一个字符串。之所以说是大部分而不是全部,是因为有一部分动态创建(concat)的string,是不会进入这样一个虚拟的hash Table中的。本文的最后附上String类的源代码,有兴趣的同学可以研究研究。
这就带来了最主要的问题,你无法准确控制或者预测一个特定字符串的生命周期。一个以string形式呈现的敏感数据(比如密码)很有可能在内存中一直存在,而你却预测它在超出某个特定函数的作用域的时候就被垃圾回收了。这样,当发生操作系统换页的时候(而这也往往是可能发生的),这个敏感数据就被保存到本地文件pagefile。sys当中,或者当操作系统休眠的时候,敏感数据进入hiberfil.sys中。一个可能的敏感数据泄漏过程是:
使用SecureString类
现在既然String靠不住了,我们能有什么简单的方法来特别的保护我的敏感数据吗? 幸运的是,.NET从Version 2.0开始,为我们提供了一套基于DPAPI的解决方法 - SecureString。
SecureString类具有以下特性:
SecureString中的内容是加密之后的,而不是平文;
使用Windows的加密方案DPAPI ;
SecureString只能在基于NT的平台上使用