C#中机密文本的保存方案

简介: 托管代码中的字符串是一类特殊的对象,它不可被改变的,每次使用 System.String 类中的方法之一或进行运算时(如赋值、拼接等)时,都要在内存中创建一个新的字符串对象,也就是为该新对象分配新的空间。

托管代码中的字符串是一类特殊的对象,它不可被改变的,每次使用 System.String 类中的方法之一或进行运算时(如赋值、拼接等)时,都要在内存中创建一个新的字符串对象,也就是为该新对象分配新的空间。这就带来两个问题:

1:原来的字符串是不是还在内存当中?

2:如果在内存当中,那么机密数据(如密码)该如何保存才足够安全?

 

先来看第一个问题:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
 
  
public class Program
{
static void Main( string [] args)
{
Method1();
// 在此处打上断点
Console.ReadKey();
}
static void Method1()
{
string str = " luminji " ;
Console.WriteLine(str);
}
}

 

在Method1处打上断点,让VS执行到此处,在即时窗口中运行命令:.load sos.dll 和 !dso,如下:

image

打开调试中的内存查看窗口,定位到019db820(由!dso得到)。由于此时还没有进入到Method1,所以内存当中不存在字符串“luminji”。接着让程序运行到方法内部,我们看到内存当中已经存在了“luminji”了。

image
接着让程序继续运行,退出方法Method1,发现“luminji”依然留在内存当中。

这就带来一个问题,如果有恶意人员扫描你的内存,你的程序所保存的机密信息将无处可逃。幸好FCL中提供了System.Security.SecureString,SecureString表示一个应保密的文本,在初始化时就已经被加密。

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
 
  
public class Program
{
static System.Security.SecureString secureString = new System.Security.SecureString();

static void Main( string [] args)
{
Method2();
// 在此处打上断点
Console.ReadKey();
}
static void Method2()
{
secureString.AppendChar(
' l ' );
secureString.AppendChar(
' u ' );
secureString.AppendChar(
' m ' );
secureString.AppendChar(
' i ' );
secureString.AppendChar(
' n ' );
secureString.AppendChar(
' j ' );
secureString.AppendChar(
' i ' );
}
}

 

相同的方法,可以发现在进入Method2后,已经找不到对应的字符串了。但是,问题随之而来,核心数据的保存问题已经解决了,可是文本总是要取出来用的,只要取出来不是就会被发现吗。没错,这个问题没法避免,但是我们可以做到文本一使用完毕,就释放掉。

见如下代码:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
 
  
static void Method3()
{
secureString.AppendChar(
' l ' );
secureString.AppendChar(
' u ' );
secureString.AppendChar(
' m ' );
secureString.AppendChar(
' i ' );
secureString.AppendChar(
' n ' );
secureString.AppendChar(
' j ' );
secureString.AppendChar(
' i ' );
IntPtr addr
= Marshal.SecureStringToBSTR(secureString);
string temp = Marshal.PtrToStringBSTR(addr);
// 使用该机密文本do something
/// =======开始清理内存
// 清理掉非托管代码中对应的内存的值
Marshal.ZeroFreeBSTR(addr);
// 清理托管代码对应的内存的值(采用重写的方法)
int id = GetProcessID();
byte [] writeBytes = Encoding.Unicode.GetBytes( " xxxxxx " );
IntPtr intPtr
= Open(id);
unsafe
{
fixed ( char * c = temp)
{
WriteMemory((IntPtr)c, writeBytes, writeBytes.Length);
}
}
/// =======清理完毕
}

 

注意查看上文代码:

    IntPtr addr = Marshal.SecureStringToBSTR(secureString);
    string temp = Marshal.PtrToStringBSTR(addr);

这两行代码表示的就是将机密文本从secureString取出来,临时赋值给字符串temp。这就存在两个问题,第一行实际调用的是非托管代码,它在内存中也会存储一个“luminji”,第二行代码是在托管内存中存储一个“luminji”。这两段文本的释放方式是不一样的。前者,可以通过使用:

Marshal.ZeroFreeBSTR(addr);

进行释放。而托管内存中的文本,只能通过重写来完成(如上文中,就是重写成为无意义的“xxxxxx”)。

上段代码涉及到的几个方法如下:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
 
  
public static int GetProcessID()
{
Process p
= Process.GetCurrentProcess();
return p.Id;
}
public static IntPtr Open( int processId)
{
IntPtr hProcess
= IntPtr.Zero;
hProcess
= ProcessAPIHelper.OpenProcess(ProcessAccessFlags.All, false , processId);
if (hProcess == IntPtr.Zero)
throw new Exception( " OpenProcess失败 " );
processInfo.hProcess
= hProcess;
processInfo.dwProcessId
= processId;
return hProcess;
}
public static int WriteMemory(IntPtr addressBase, byte [] writeBytes, int writeLength)
{
int reallyWriteLength = 0 ;
if ( ! ProcessAPIHelper.WriteProcessMemory(processInfo.hProcess, addressBase, writeBytes, writeLength, out reallyWriteLength))
{
// throw new Exception();
}
return reallyWriteLength;
}

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[Flags]
enum ProcessAccessFlags : uint
{
All
= 0x001F0FFF ,
Terminate
= 0x00000001 ,
CreateThread
= 0x00000002 ,
VMOperation
= 0x00000008 ,
VMRead
= 0x00000010 ,
VMWrite
= 0x00000020 ,
DupHandle
= 0x00000040 ,
SetInformation
= 0x00000200 ,
QueryInformation
= 0x00000400 ,
Synchronize
= 0x00100000
}
static class ProcessAPIHelper
{
[DllImport(
" kernel32.dll " )]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
[DllImport(
" kernel32.dll " , SetLastError = true )]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte [] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
[DllImport(
" kernel32.dll " , SetLastError = true )]
public static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[Out]
byte [] lpBuffer,
int dwSize,
out uint lpNumberOfBytesRead
);
[DllImport(
" kernel32.dll " , SetLastError = true )]
[
return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
}

 

 

总结:

1:机密文本使用System.Security.SecureString保存;

2:System.Security.SecureString被释放后使用Marshal.ZeroFreeBSTR清除在内存中的痕迹;

3:托管字符串只能使用重写内存进行清除;

有关利用sos.dll调试非托管代码,查看http://www.cnblogs.com/luminji/archive/2011/01/27/1946217.html

Creative Commons License本文基于 Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 http://www.cnblogs.com/luminji(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。
目录
相关文章
|
1月前
|
存储 JSON Kubernetes
在K8S中,存储敏感信息方式有哪些?
在K8S中,存储敏感信息方式有哪些?
|
3月前
|
存储 网络协议 文件存储
技术心得:图片存储方案
技术心得:图片存储方案
51 0
|
4月前
|
存储 缓存 固态存储
|
4月前
|
存储 数据安全/隐私保护
平台设计-联系信息的存储
之前说过平台的用户信息是集中存储的
|
4月前
|
SQL 数据可视化 数据库
聊天记录年度报告一览无余:轻松多格式导出永久保存,深度智能分析
聊天记录年度报告一览无余:轻松多格式导出永久保存,深度智能分析
聊天记录年度报告一览无余:轻松多格式导出永久保存,深度智能分析
替换文件中的敏感信息
假设我们有一份文件,文件中包含了很多个人信息。现在需要一份去除其中敏感信息的版本,将文件中所有手机号的4~7位和身份证号的6~15位用 * 替换。
|
数据可视化 数据挖掘
数据信息汇总的7种基本技术总结
数据汇总是一个将原始数据简化为其主要成分或特征的过程,使其更容易理解、可视化和分析。本文介绍总结数据的七种重要方法,有助于理解数据实质的内容。
204 0
|
存储 安全 算法
在日常开发中,敏感数据应该如何保存或传输
说到敏感信息,第一个想到的恐怕就是用户密码了吧。攻击者一旦获取到了用户密码,就会登录用户的账号进行一系列操作。甚至有些用户还习惯不管什么应用都用同一个密码,导致攻击者可以登录用户全网账号。
|
大数据 定位技术 数据库
《位置大数据隐私管理》—— 1.5 典型的位置隐私保护技术
传统的LBS隐私保护技术可以归纳为3类:基于数据失真的位置隐私保护方法、基于抑制发布的位置隐私保护方法以及基于数据加密的位置隐私保护方法。不同的位置隐私保护技术基于不同的隐私保护需求以及实现原理,在实际应用中各有优缺点。
4330 0