在控制台读取用户输入密码,你会么?
客官请听题,请写一段代码从控制台上读取用户输入密码。不过有几点在实现的时候需要特别注意:
1. 最基本的是用户输入的不应该直接明文显示出来,需要用特殊字符显示。
2. 不要用明文储存用户输入的密码。
3. 要考虑到一些特殊按键的输入。
自己写写试试看?
下面是我的实现:
private SecureString ReadPassword() { ConsoleKeyInfo key; SecureString password = new SecureString(); key = Console.ReadKey(true); while (key.Key != ConsoleKey.Enter) { //如果用户退格需要将最后一个字符在界面上和字符串里同时删除 if (key.Key == ConsoleKey.Backspace) { if (password.Length > 0) { password.RemoveAt(password.Length - 1); //利用控制字符\b退格,但是退格是不会删除最后一个字符的,用空格覆盖了最后一个字符。 Console.Write("\b \b"); } } //忽略特殊控制字符,比如说tab, esc等 else if (key.KeyChar >= 32 && key.KeyChar <= 126) { password.AppendChar(key.KeyChar); Console.Write("*"); } key = Console.ReadKey(true); } Console.WriteLine(); password.MakeReadOnly(); return password; }
关于SecureString
看代码大概都能理解了,我想说的其实是关于SecureString这个类。之前没有接触过这个类,只知道存敏感信息的时候应该用这个类而不是直接用String去储存。
我们为什么要用SecureString去存敏感信息,为什么不直接用String?
- SecureString中的数据会固定在内存中,不会随着GC的发生而挪动位置,这样就防止了敏感信息在内存有多处的拷贝。而对于String来说它是不会固定在内存中的,也就是说很可能它会被GC多次移动从而在内存中存在多分拷贝。
- 相比较String,SecureString不是以明文的形式储存在内存中的而是使用了DPAPI[1]进行加密。即使你的内存被交换到了页交换文件中也不会以明文的形式存在。SecueString使用用户的一些信息进行加密(登录的session,进程等信息)[2],这也就意味着如果你的程序出问题了你可以放心的将dump发给你的服务商而不必担心他们会看到你的储存在字符串中的敏感信息。
- String是不可变的,这也就意味着当你修改信息的时候同时会有新旧两个版本的信息存在于内存中。而SecureString没有这个问题。
- 另外由于String是不可变的,那么当不需要使用的时候清理起来也比较麻烦。SecureString可以在不使用的情况下将内存置为0来保护数据。
使用SecureString就绝对安全么?
当然不是,世上没有绝对安全这种事情,SecureString只是通过上面这几点增加了获取敏感信息的难度。如果说有人能够访问你的进程内存,他就有办法获得SecureString的内存,既然都到了这一步了应该就有办法有能力去破解了我觉得(我是不会的)。他也可以等待程序啥时候将敏感信息解析为明文的时候获取。
总结:
- 在涉及到一些敏感信息的时候应该尽可能的使用SecureString来储存数据,当然敏感信息的储存方式就是另一个话题了。
- 我觉得微软可以考虑给Console提供一个读取加密信息的接口,类似于 public static SecureString ReadSecureString。
- 关于SecureString还有很多其他的内容,以后慢慢研究。
阅读资料:
Windows Data Protection, http://msdn.microsoft.com/en-us/library/ms995355.aspx
引用:
[1]: Making Strings More Secure, http://blogs.msdn.com/b/shawnfa/archive/2004/05/27/143254.aspx#144606
[2]: SecureString Redux, http://blogs.msdn.com/b/shawnfa/archive/2006/11/01/securestring-redux.aspx