二十九:安全字符串
String对象可能包含一些敏感数据,比如用户密码等,如果允许执行一些不安全或者非托管的代码,这些代码就可以扫描进程的地址空间,找到包含敏感数据的字符串,并以一种非授权的方式来使用这些数据,即使String对象只是使用一小段时间,然后就会垃圾收集器收集,CLR也可能无法立即使用String对象的内存,致使String对象的字符长时间保留在进程的内存中,造成机密数据泄露,而且,由于String对象是不可变的,所以当处理它们时,旧的副本会留在内存中,以最终造成不同版本的字符串散布在整个内存空间中。
为了安全,FCL中有一个更安全的字符串类System.Security.SecureString,构造一个SecureString对象时,它会在内存分配一个非托管内存块,其中包含一个字符数组,之所以要使用非托管内存,是为了避开垃圾收集器。这些字符串是经过加密的,能防范任何恶意的非托管代码获取机密信息。利用以下任何一个方法,即可在安全字符串中追回,插入,删除或者设置一个字符:AppendChar,InsertAt,RemoveAt,SetAt,调用这些方法时,方法内部会会解密字符,执行指定操作,然后重新加密字符,而会对应用程序的性能造成影响。
SecureString类实现了IDisposable接口,以便提供一种简单的方式来确定性的摧毁字符串中的安全内容。当应用程序不再需要敏感的字符串信息时,只需要调用SecureString的Dispose方法,在内部,Dispose会清零内存缓冲区的内容,确保恶意代码无法获得毛敏感信息,然后释放缓冲区。另外要注意的是,SecureString类是从CriticalFinalizerObject类派生的,和String对象不同,在回收一个SecureString对象时,加密字符串的内容将不存在于内存中。
在.NET Framework 2.0版本中,可以在以下情况下将SecureString作为一个密码来传递:
(1)与一个加密服务提供者协作
(2)创建、导入、导出一个X.509证书。
(3)在特定用户下启动一个新进程。
(4)不能在Window窗体、Wed窗体等中使用SecureString
我样可以创建一个方法来接受一个SecureString对象参数,在方法内部,必须让SecureString对象创建一个非托管内存缓冲区,并且其中包含解密过的字符,然后才能让该方法使用缓冲区,我们的代码在访问解密的字符串时,经历的时间应尽量的短,结束使用字符串后,代码应尽快清零并释放缓冲区,而且绝不能将SecureString的内容放到一个String中,SecuteString类没有专门重写ToString方法来避免泄露敏感数据。
以下代码演示了如何初始化和使用一个SecureString,编译它是,要为C#编译器指定/unsafe开关: