将敏感信息加载到协议缓冲区的最安全方式

Most secure way to load sensitive information into protocol buffers

我的应用程序使用 Google 协议缓冲区在客户端和服务器实例之间发送敏感数据。网络 link 是用 SSL 加密的,所以我不担心网络上的窃听者。由于内存问题,我担心将敏感数据实际加载到 protobuf in this SO question

例如:

Login login = Login.newBuilder().setPassword(password)// problem
                                .build();

由于协议缓冲区是不可变的,因此没有办法安全地执行此操作吗?

Protobuf 不提供任何使用 char[] 而不是 String 的选项。相反,Protobuf 消息有意设计为完全不可变,这提供了一种不同的安全性:您可以在程序的多个沙盒组件之间共享单个消息实例,而不必担心一个人可能会修改数据以干扰另一个人.

作为一名安全工程师,我个人认为——尽管其他人会不同意——SO 问题中描述的 "security" 你 link 是安全剧院,实际上不值得追求,因为多种原因:

  1. 如果攻击者可以读取您进程的内存,那么您就已经输了。即使您在丢弃秘密之前覆盖了它的内存,如果攻击者在正确的时间读取您的内存,他们也会找到密码。但是,更糟糕的是,如果攻击者能够读取您进程的内存,他们可能会做比提取临时密码更糟糕的事情:他们可能会提取长期存在的秘密(例如您服务器的 TLS 私钥) ,覆盖部分内存以更改您的应用程序的行为,访问您的应用程序有权访问的任何和所有资源等。这根本不是一个可以通过在使用后将某些字段归零来有意义地解决的问题。

  2. 实际上,有太多方法可以复制您的秘密,而您无法控制这些方法,使整个练习变得毫无意义:

    • 即使您很小心,垃圾收集器也可能在移动内存时复制秘密,从而破坏目的。为避免这种情况,您可能需要使用由非托管内存支持的 ByteBuffer
    • 当将数据读入您的进程时,它几乎肯定会通过不会以这种方式覆盖其数据的库代码。例如,InputStream 可能会进行内部缓冲,之后可能不会将其缓冲区清零。
    • 操作系统可能随时将您的数据分页以交换 space 到磁盘上,并且之后没有义务将该数据清零。因此,即使您将内存归零,它也可能会持续存在于交换中。 (加密交换确保这些秘密在系统关闭时有效地消失,但不一定能防止本地机器上存在的攻击者能够从内核中提取交换加密密钥。)
    • 等等

因此,在我看来,在 Java 中专门使用可变对象来以这种方式覆盖机密并不是一个有用的策略。这些威胁需要在其他地方解决。