8
String secret="foo";
WhatILookFor.securelyWipe(secret);

而且我需要知道它不会被java优化器删除。

4

4 回答 4

8

字符串不能被“擦除”。它是不可变的,缺少一些非常肮脏和危险的技巧,你无法改变它。

所以最安全的解决方案是首先不要将数据放入字符串中。改用 StringBuilder 或字符数组,或其他一些不可变的表示。(然后在完成后清除它。)


作为记录,有几种方法可以更改字符串的后备数组的内容。例如,您可以使用反射来找出对 String 的支持数组的引用,并覆盖其内容。但是,这涉及到执行 JLS 状态未指定行为的事情,因此您不能保证优化器不会做意外的事情。


我个人对此的看法是,您最好锁定您的应用程序平台,以便未经授权的人首先无法访问内存/内存转储。毕竟,如果平台没有得到适当的保护,“坏人”可能会在你删除之前获得字符串内容。对于少量的安全关键状态,可能需要这样的步骤,但是如果您有很多“机密”信息要处理,那么无法使用正常的字符串和字符串处理将是一个主要的麻烦。

于 2012-08-17T14:53:50.303 回答
6

您将需要直接访问内存。

您真的无法使用 String 执行此操作,因为您无法可靠地访问该字符串,并且不知道它是否已在某处实习,或者是否创建了您不知道的对象。

如果你真的需要这个,你必须做类似的事情

public class SecureString implements CharSequence {
    char[] data;
    public void wipe() {
       for(int i = 0; i < data.length; i++) data[i] = '.'; // random char
    }
}

话虽如此,如果您担心数据仍在内存中,您必须意识到,如果它曾经在内存中,那么攻击者可能已经得到了它。您真正保护自己免受的唯一事情是核心转储被刷新到日志文件中。

关于优化器,我非常怀疑它会优化操作。如果你真的需要它,你可以这样做:

public int wipe() {
    // wipe the array to a random value
    java.util.Arrays.fill(data, (char)(rand.nextInt(60000));
    // compute hash to force optimizer to do the wipe
    int hash = 0;
    for(int i = 0; i < data.length; i++) {
        hash = hash * 31 + (int)data[i];
    }
    return hash;
}

这将强制编译器进行擦除。它使它的运行时间大约是两倍,但它是一个相当快的操作,并且不会增加复杂性的顺序。

于 2012-08-17T14:52:58.423 回答
3

使用“不安全”方法将数据存储在堆外。然后,您可以在完成后将其归零,并确保它不会被 JVM 推送到堆周围。

这是一篇关于不安全的好帖子:

http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/

于 2012-08-17T14:52:09.447 回答
0

如果您要使用字符串,那么我认为您担心它会出现在内存转储中。我建议String.replace()在关键字符上使用,以便在运行时使用 String 时,它会发生变化,然后在使用后超出范围,并且不会在内存转储中正确显示。但是,我强烈建议您不要将字符串用于敏感数据。

于 2012-08-17T14:57:44.797 回答