通过在垃圾收集之前使用反射清理内容来安全地使用字符串作为密码
使用反射擦洗 String
是否使使用 String
与使用 char[]
密码一样安全?
Does using reflection to scrub a String
make using String
as safe as using char[]
for passwords?
从安全方面来看,通常认为最好的做法是使用 char[]
来存储/传递密码,因为可以在代码中尽快将其内容归零,这可能是在垃圾收集清理它并重用内存(擦除所有跟踪)之前显着降低内存攻击的时间窗口.
From a security aspect, it is generally considered best practice to use char[]
for storing/passing passwords, because one can zero-out its contents as soon as possible in code, which may be significantly before garbage collection cleans it up and the memory is reused (wiping all trace), limiting the window of time for a memory attack.
但是,char[]
不如 String
方便,所以如果可以擦洗"一个 String
需要,从而使 String
与 char[]
一样安全.
However, char[]
is not as convenient as String
, so it would be handy if one could "scrub" a String
if needed, thus making String
as safe as char[]
.
以下是使用反射将 String
的字段清零的方法.
Below is a method that uses reflection to zero-out the fields of String
.
这个方法OK"吗,它是否达到了使 String
与 char[]
一样安全的目标?
Is this method "OK", and does it achieve the goal of making String
as safe as char[]
for passwords?
public static void scrub(String str) throws NoSuchFieldException, IllegalAccessException {
Field valueField = String.class.getDeclaredField("value");
Field offsetField = String.class.getDeclaredField("offset");
Field countField = String.class.getDeclaredField("count");
Field hashField = String.class.getDeclaredField("hash");
valueField.setAccessible(true);
offsetField.setAccessible(true);
countField.setAccessible(true);
hashField.setAccessible(true);
char[] value = (char[]) valueField.get(str);
// overwrite the relevant array contents with null chars
Arrays.fill(value, offsetField.getInt(str), countField.getInt(str), '');
countField.set(str, 0); // scrub password length too
hashField.set(str, 0); // the hash could be used to crack a password
valueField.setAccessible(false);
offsetField.setAccessible(false);
countField.setAccessible(false);
hashField.setAccessible(false);
}
这是一个简单的测试:
String str = "password";
scrub(str);
System.out.println('"' + str + '"');
输出:
""
注意:您可以假设密码不是 String
常量,因此调用此方法不会对实习字符串产生不利影响.
Note: You may assume that passwords are not String
constants and thus calling this method will have no adverse effect on interned Strings.
另外,为了简单起见,我将方法保留为相当原始"的状态.如果我要使用它,我不会声明抛出的异常(尝试/捕获/忽略它们)并重构重复的代码.
Also, I have left the method is a fairly "raw" state for simplicity's sake. If I were to use it, I would not declare exceptions thrown (try/catch/ignoring them) and refactor repeated code.
推荐答案
有两个潜在的安全问题:
There are two potential safety concerns:
字符串可以与其他字符串共享它的支持数组;例如如果
String
是通过在较大的String
上调用substring
创建的.因此,当您将整个value
数组归零时,您可能会覆盖其他不包含密码的字符串的状态.
The String may share its backing array with other Strings; e.g. if the
String
was created by callingsubstring
on a largerString
. So when you zero the entirevalue
array you could be overwriting the state of other strings ... that don't contain passwords.
解决方法是仅将密码字符串使用的后备数组部分归零.
The cure is to only zero the part of the backing array that is used by the password string.
JLS (17.5.3) 警告使用反射来更改 final
变量的效果未定义.
The JLS (17.5.3) warns that the effects of using reflection to change final
variables is undefined.
然而,它的上下文是 Java 内存模型,以及允许编译器积极缓存 final
变量的事实.在这种情况下:
However, the context for this is the Java Memory Model, and the fact that the compiler is allowed to aggressively cache final
variables. In this case:
你会期望 String 是线程受限的,而
you would expect the String to be thread-confined, and
您不应该再次使用这些变量.
you shouldn't be using any of those variables again.
我不希望这些都是真正的问题...模修正 value
的过度归零.
I wouldn't expect either of these to be real problems ... modulo fixing the over-aggressive zeroing of value
.
但真正令人担忧的是 Velociraptors.:-)
But the real concern is Velociraptors. :-)
我很困惑,你居然会费心像这样破解密码.当您考虑它时,您要防止的是有人可以读取进程内存......或核心转储或交换文件......以检索密码的可能性.但是,如果有人可以做到这一点,那么您的系统安全性肯定已经受到威胁……因为这些东西很可能需要 root
访问权限(或同等权限).如果他们有 root
访问权限,他们可以调试"您的程序并在您的应用程序破坏它们之前捕获密码.
I'm puzzled that you would actually bothering to zap passwords like this. When you think about it, what you are protecting against is the possibility that someone can read process memory ... or a core dump or swap file ... to retrieve passwords. But if someone can do that, your system security has to have already been compromised ... cos' those things most likely require root
access (or equivalent). And if they have root
access they can "debug" your program and catch the passwords before your application zaps them.
相关文章