在 Swing 中,密码字段有一个getPassword()
(返回char[]
)方法,而不是通常的getText()
(返回String
)方法。同样,我遇到了一个不使用String
来处理密码的建议。
为什么String
在密码方面会对安全构成威胁?使用char[]
感觉不方便。
字符串是不可变的 。这意味着一旦你创建了String
,如果另一个进程可以转储内存,那么除了反射之外没有办法可以在垃圾收集开始之前摆脱数据。
使用数组,您可以在完成数据后显式擦除数据。您可以使用您喜欢的任何内容覆盖数组,并且即使在垃圾回收之前,密码也不会出现在系统的任何位置。
所以是的,这是一个安全问题 - 但即使使用char[]
只会减少攻击者的机会窗口,而且只针对这种特定类型的攻击。
正如评论中所指出的那样,垃圾收集器移动的数组可能会将数据的杂散副本留在内存中。我相信这是特定于实现的 - 垃圾收集器可以清除所有内存,以避免这种情况。即使它确实存在,仍然有时间char[]
包含实际字符作为攻击窗口。
虽然这里的其他建议似乎有效,但还有另一个好的理由。使用普通String
您可能会意外地将密码打印到日志 ,监视器或其他一些不安全的地方。 char[]
不那么脆弱。
考虑一下:
public static void main(String[] args) {
Object pw = "Password";
System.out.println("String: " + pw);
pw = "Password".toCharArray();
System.out.println("Array: " + pw);
}
打印:
String: Password
Array: [C@5829428e
引用官方文档, Java Cryptography Architecture 指南说明了char[]
与String
密码(关于基于密码的加密,但当然更常见的是关于密码):
将密码收集并存储在
java.lang.String
类型的对象中似乎是合乎逻辑的。但是,这里有一个警告:String
类型的Object
是不可变的,即没有定义的方法允许您在使用后更改(覆盖)或清零String
的内容。此功能使String
对象不适合存储安全敏感信息,如用户密码。您应该始终在char
数组中收集和存储安全敏感信息。
Java 编程语言安全编码指南的准则 2-2,版本 4.0也说了类似的东西(虽然它最初是在日志记录的上下文中):
准则 2-2:不要记录高度敏感的信息
某些信息(如社会安全号码(SSN)和密码)非常敏感。即使是管理员,也不应将此信息保存的时间超过必要的时间,也不应保存在何处。例如,它不应该发送到日志文件,并且不应通过搜索检测到它的存在。一些瞬态数据可以保存在可变数据结构中,例如 char 数组,并在使用后立即清除。清除数据结构降低了典型 Java 运行时系统的有效性,因为对象在内存中透明地移动到程序员。
本指南还对低级库的实现和使用有影响,这些低级库没有他们正在处理的数据的语义知识。例如,低级字符串解析库可以记录它所处理的文本。应用程序可以使用库解析 SSN。这会产生一种情况,即管理员可以使用 SSN 访问日志文件。
字符串是不可变的 。这意味着一旦创建了String
,如果另一个进程可以转储内存,就无法(除了Reflection )在垃圾回收开始之前就摆脱掉数据。
使用数组,您可以在使用完数据后显式擦除数据。您可以用任何您喜欢的东西覆盖阵列,并且即使在垃圾回收之前,密码也不会出现在系统中的任何位置。
因此,是的,这是一个安全性问题 - 但是即使使用char[]
只会减少攻击者的机会之窗,并且仅针对这种特定类型的攻击。
如注释中所述,垃圾回收器移动的数组可能会将数据的零散副本保留在内存中。我相信这是特定于实现的 - 垃圾收集器可能会清除所有内存,以免发生这种情况。即使这样做,仍然有一段时间char[]
包含实际角色作为攻击窗口。
尽管这里的其他建议似乎是有效的,但还有另一个很好的理由。使用纯String
您更有可能不小心将密码打印到日志 ,监视器或其他不安全的地方。 char[]
不太容易受到攻击。
考虑一下:
public static void main(String[] args) {
Object pw = "Password";
System.out.println("String: " + pw);
pw = "Password".toCharArray();
System.out.println("Array: " + pw);
}
印刷品:
String: Password
Array: [C@5829428e
为了引用正式文档,《 Java 密码学体系结构指南》说这是关于char[]
与String
密码(关于基于密码的加密,但这当然更普遍地是关于密码):
收集密码并将其存储在
java.lang.String
类型的对象中似乎合乎逻辑。但是,有一个警告:String
类型的Object
是不可变的,即,没有定义的方法允许您在使用后更改(覆盖)或将String
的内容归零。此功能使String
对象不适合存储对安全敏感的信息,例如用户密码。您应该始终将安全敏感信息收集并存储在char
数组中。
版本 4.0 的 Java 编程语言安全编码指南的指南 2-2也表示类似的内容(尽管它最初是在日志记录的上下文中):
准则 2-2:请勿记录高度敏感的信息
一些信息,例如社会安全号码(SSN)和密码,是高度敏感的。该信息的保存时间不应超过必要的时间,也不能保存在任何地方,即使是管理员也是如此。例如,不应将其发送到日志文件,并且不应通过搜索来检测其存在。某些瞬态数据可以保存在可变数据结构中,例如 char 数组,并在使用后立即清除。清除数据结构在典型的 Java 运行时系统上降低了效率,因为对象在内存中对程序员透明地移动。
该指南还对不具有所处理数据语义知识的低层库的实现和使用产生影响。例如,低级字符串解析库可能会记录其工作的文本。应用程序可以使用库解析 SSN。这将导致管理员可以访问日志文件使用 SSN。