如何在C#的Main函数中处理作为命令行参数的密码?
解决方法
方案1:替换传参方式(最优选择)
命令行参数本身会在操作系统的进程信息中留存公开副本,哪怕你清理了程序内部的字符串内存,系统层面的记录也无法清除,泄露风险更高,优先选择这种方式规避问题:
- 改为交互式无回显输入密码,通过
Console.ReadKey(true)逐字符读取用户输入,直接存入char[]数组,不需要经过不可变的string类型中转 - 密码校验逻辑执行完成后,直接遍历
char[]覆写为\0即可完全清空内存数据
参考代码示例:
List<char> inputChars = new List<char>(); ConsoleKeyInfo keyInfo; Console.WriteLine("请输入待校验密码:"); do { keyInfo = Console.ReadKey(intercept: true); if (!char.IsControl(keyInfo.KeyChar)) { inputChars.Add(keyInfo.KeyChar); } } while (keyInfo.Key != ConsoleKey.Enter); char[] password = inputChars.ToArray(); // 此处执行密码哈希计算、调用haveibeenpwned API的逻辑 // 用完直接清空char数组 Array.Fill(password, '\0'); Array.Fill(inputChars.ToArray(), '\0');
方案2:必须使用命令行参数时的unsafe覆写方案
如果确实需要通过Main函数的string[] args传参,可以通过不安全代码直接覆写字符串的堆内存,绕过C#对string的不可变限制:
- 该方案依赖.NET运行时的string内存布局实现,需要项目开启「允许不安全代码」的编译选项
- 只能清理程序托管堆内的string副本,操作系统进程信息中留存的命令行参数记录无法清除,仍然有泄露风险
参考代码示例:
// 拿到密码参数后第一时间处理,处理完立刻调用该方法清内存 unsafe static void OverwriteStringContent(string str) { fixed (char* ptr = str) { for (int i = 0; i < str.Length; i++) { ptr[i] = '\0'; } } }
注意事项
- 处理密码过程中不要对原始密码字符串做任何拼接、截取、ToString等操作,避免生成新的不可变string副本,导致无法完全清理
- 对接haveibeenpwned API时仅传输密码SHA1哈希的前5位,不要传输完整密码或完整哈希值
内容的提问来源于stack exchange,提问作者Jackson Tarisa
相关产品推荐
相关产品推荐

