清除所有线程中的数组

时间:2019-06-30 15:15:58

标签: java

在Java中,建议使用int64存储密码或其他敏感信息,以便在不再需要数据时可以手动将其清除。

如何在所有线程中清除这样的数组?如果我理解正确,线程可能只会在其缓存中执行更改,而不会在共享内存中执行更改,因此以下操作将无法可靠地工作:

-1
  • 此假设是否正确?线程是否总是写入共享内存?
  • 是否需要使用char[](或其他同步)来确保共享内存已更新?
    • 为此是否需要先发生关系,因为编译器/ JVM否则会由于优化而忽略内存同步?
  • 其他线程是否必须建立 happens-before 关系以从其缓存中清除数组内容,还是可以忽略不计?可能是因为缓存将用于其他更频繁访问的数据,并且由于不再使用该数组,因此该数组将被丢弃。

编辑:关于char[] password = ... ... Arrays.fill(password, '\0'); 应该用作密码的说法是基于Why is char[] preferred over String for passwords?的,但是再次查看后,这还是有争议的。

2 个答案:

答案 0 :(得分:2)

使数组引用为volatile不能保证对它的内容的volatile访问。如果您想要线程安全的共享访问,则可以使用AtomicIntegerArray。否则,您可能希望将char数组包装到自定义类中,并围绕其方法进行同步。尽管后者的性能会降低。

请注意,使用字符数组而不是字符串可能并非真正安全。如果攻击者可以访问您的计算机,并且在这样做的情况下,您还有很多更严重的顾虑,那么在char数组包含数据的时候仍可以转储进程内存。另外,垃圾收集可能会在压缩阶段将您的数据移动到其他位置,从而将密码保留在尚未被覆盖的已释放“垃圾”内存中(假设您正在谈论线程之间的共享成员,则自从您char数组将被认为是长期存在的,并将其复制到为较早的对象保留的内存空间中。

答案 1 :(得分:1)

我认为jbx有一个很好的答案。如果攻击者可以访问您的主内存,则与担心内存中的密码字符串不正确相比,您可能会遇到更大的问题。担心L3缓存似乎也太过劳累了。

但是出于代码的利益,我将指出,尽管创建数组volatile不会有帮助,但是几乎所有其他形式的同步都将有所帮助。它们都具有要求所有写入可见的语义。因此,您可以保证对阵列的更改是可见的。

public class Password {

  private final char[] password;

  public Password( char[] p ) {
     password = Arrays.copy( p, p.length );
  }

  public synchronized boolean compare( char[] p ) {
      return Arrays.equal( password, p );
  }

  public synchronized clear() {
    Arrays.fill(password, 42 );
  }
}

代码未经测试。

在这里,我使用synchronized只是为了提供内存可见性。原子性只是一个副作用,可能不需要。