为了安全起见,将 new String(char[]) 作为密码参数传递给只接受字符串作为参数的方法是否安全?

For security, is it safe to pass new String(char[]) as an argument for passwords to a method that only accepts string as parameter?

我读过关于密码,使用 char[] 更理想,因为 Strings 是不可变的。 问题是,我需要将密码参数传递给调用 http 调用的第三方依赖项。它只接受字符串。该调用涉及在消息中发送密码,因此我使用方法:

new Request.Builder()
    .url("notmyapi.com/login")
    .method("POST", new FormBody.Builder()
        .add("name", username)
        .add("password", password)
        .build())
    .build()

我知道网络 api 只是按原样接受密码而不是接受 base64 版本或其他任何东西可能很糟糕,但这不是我的,我只需要做我的工作。

问题:

由于我现在将密码作为 char[] 传递,但是上面的 .add(key, value) 方法只接受一个字符串,所以 .add("password", new String(password)) 安全吗?这会不会在内存中创建一个新的 String 实例,我应该出于安全考虑而试图避免这种情况?

I've read that for passwords, it's more ideal to use char[] since Strings are immutable.

这不是(全部)原因。实际推理是:

  1. 您正在使用密码以确保安全。

  2. 坏人可能能够获取 运行 JVM 的内存转储...或从保存(或保存)JVM 内存的磁盘块中提取部分转储页。该转储可能包含明文密码。

  3. 为了缓解这一问题,可以(应该)在不再需要密码时覆盖包含密码的内存。

  4. 很难覆盖 String 因为它是不可变的。

但是,如果您检查其中的每一点,就会发现一些假设可能会受到质疑。

  1. 密码不是实现安全性的好方法。对于高级安全性,您应该使用 public/private 密钥或密钥身份验证。因此,要么您是在谈论对您的系统来说 "less important" 的密码……要么您遇到的问题比您要在此处解决的问题更大。

  2. 如果坏人可以读取 JVM 内存或页面文件,那么他们已经深入到您的基础设施中了。你已经致命地妥协了。此外,如果他们陷得这么深,他们很可能会找到其他方法来窃取密码;例如窃取您的 SSL 证书私钥并窥探/解码加密的网络流量。

  3. 这只是一种缓解措施:

    • 密码将在 char[] 一段时间内保持明文。
    • 在密码到达您的代码之前,它们很可能已被 servlet 堆栈(或您使用的任何框架)转换为 String 对象。
    • 此外,此缓解措施仅在您 覆盖代码中的 char[] 时才有效。
  4. 其实如果你真的很担心这个,可以使用反射来擦除一个String;即改变 String 对象。一般来说,这是一件危险的事情。对于密码/密码检查,这可能是可以接受的。

简而言之,我认为使用 char[] 而不是 String 在 Java 中实施太昂贵了....除非你准备好完全重新设计你的网络框架。此外,考虑到具有深度访问权限的人可以通过其他方式窃取密码,这样做的好处值得商榷。


现在回答您的具体问题:

Is it safe to do .add("password", new String(password))

如果您认为使用 String 作为密码存在风险,那么该代码就无济于事了。事实上,假设 password 是一个 String,您现在在内存中有两个包含密码文本的 String 对象。

  • 如果您使用的 API 要求您提供密码,因为 String 您有问题。

  • 如果密码来源(即您如何从用户界面、servlet 框架等获取密码)是 String,您就有问题了。

  • 这两种情况都需要重新处理与您交互的代码的各个方面以解决问题。这可能很难。

并且:

I know it's probably bad that the web api is just accepting a password as it is instead of accepting a base64 version or whatever.

Base64 不会减慢坏人的速度超过一分钟左右。任何可逆编码均是如此。但我希望您坚持只能通过 SSL / TLS 安全连接发送密码。

无论您在客户端如何尝试,安全性都无关紧要,因为根据您在 post 中的代码,它似乎是一个简单的 http 客户端。所以它会基于http协议做一个简单的http请求。因此,您的密码将以纯文本形式出现在 http post 请求的正文中,例如 username=john&password=12345

恕我直言,你不需要从客户端做任何事情,如果网络 api 有一个启用了 ssl 的安全端点,那么你可以使用它