日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

建议118:使用SecureString保存密钥等机密字符串

發布時間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 建议118:使用SecureString保存密钥等机密字符串 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

建議118:使用SecureString保存密鑰等機密字符串

托管代碼中的字符串是一類特殊的對象,它們不可用被改變。每次使用System.String類張的方法之一時,或者使用此類型進行運算時(如賦值、拼接等),都要在內存中創建新的字符串對象,也就是為該新對象分配新的空間。這就帶來了兩個問題:

  • 原來的字符串是不是還在內存當中?
  • 如果在內存當中,那么機密數據(如密碼)該如何保存才足夠安全?
  • 針對第一個問題,我們來看一段代碼:

    static void Method1(){string str = "liming"; Console.WriteLine(str);}static void Main(string[] args){Method1(); //在此處打上斷點Console.ReadKey();}

    在Method1方法處打上斷點。在VS中讓程序執行到此處,在即時窗口中相繼運行命令:

    .load sos.dll

    !dso

    運行結果:

    .load sos.dll
    已加載擴展 G:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll
    !dso
    PDB symbol for clr.dll not loaded
    OS Thread Id: 0x9806c (622700)
    ESP/REG? Object?? Name
    003EE700 027022a8 System.Object[]??? (System.String[])
    003EE9F4 027022a8 System.Object[]??? (System.String[])
    003EEDA0 027022a8 System.Object[]??? (System.String[])
    003EEDB8 027022a8 System.Object[]??? (System.String[])
    003EEDC4 027022a8 System.Object[]??? (System.String[])
    003EEE40 027022a8 System.Object[]??? (System.String[])
    003EEF9C 027022a8 System.Object[]??? (System.String[])
    003EEFD4 027022a8 System.Object[]??? (System.String[])
    003EF510 02701238 System.SharedStatics

    ?

    打開“調試”->“窗口”->“內存”->“內存1”窗口,找到對應Object列的內存地址"027022a8",然后在內存窗口中輸入。

    由于此時還沒有進入到Method1中,所以內存當中不存在字符串“liming”。接著讓程序運行到方法內部,可以看到內存中應經存在“liming”了。

    這就出現了一個問題,如果有人惡意掃描你的內存,程序中所保存的機密信息將無處可逃。幸好FCL提供了System.Security.SecureString,SecureString表示一個應保密的文本,它在初始化時就已經被加密了。使用SecureString的示例如下:

    static System.Security.SecureString secureString = new System.Security.SecureString();static void Method2(){secureString.AppendChar('l');secureString.AppendChar('i');secureString.AppendChar('m');secureString.AppendChar('i');secureString.AppendChar('n');secureString.AppendChar('g');}static void Main(string[] args){Method2(); Console.ReadKey();}

    使用相同的調試手法可以發現,再次進入Method2后,已經找不到對應的字符串“liming”了。但是,核心數據保存問題已經解決了,可是文本總是要取出來的,只要取出來不是就會被發現嗎?這個問題沒法避免,但是我們可以做到文本使用完畢就釋放掉,代碼如下:

    static void Method3(){secureString.AppendChar('l');secureString.AppendChar('i');secureString.AppendChar('m');secureString.AppendChar('i');secureString.AppendChar('n');secureString.AppendChar('g');IntPtr addr = Marshal.SecureStringToBSTR(secureString);string temp = Marshal.PtrToStringBSTR(addr);//使用該機密文本做一些事情///=======開始清理內存//清理掉非托管代碼中對應的內存的值 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);}}///=======清理完畢 }static PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();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;}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);}

    ?

    注意查看上文中的代碼:

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

    這兩行代碼表示的就是把機密文本從SecureString取出來,臨時賦值給字符串temp。這里存在兩個問題:第一行實際調用的是非托管代碼,它在內存中也會存儲一個“liming”;第二行代碼會在托管內存中存儲一個“liming”。這兩段文本的釋放方式是不一樣的。前者可以通過使用下面代碼釋放:

    Marshal.ZeroFreeBSTR(addr);

    而托管內存中的文本,只能通過重寫來完成(如上文中,就是重寫成無意義的“xxxxxx”了)。當然,沒有絕對的安全,因為即便如此,讓關鍵字符串在內存中像流星一樣一閃而過,它也存在被捕獲的可能性。但是我們通過這種方法降低了數據被破解的概率。

    ?

    ?

    轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技

    總結

    以上是生活随笔為你收集整理的建议118:使用SecureString保存密钥等机密字符串的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。