—+ 简介

问:如何以加密方式保护以纯文本形式存储在磁盘上的凭证文件?

或者说:我如何避免将 Gmail 和其他 API 密钥之类的凭证存储在磁盘上?对于现有程序,这些程序假设此类未加密文件包含机密信息。

我问这个问题的动机是想使用 gmi/lieer 和 notmuch 访问 Gmail – 据我所知,它们使用磁盘上未加密的凭据文件。但还有很多其他程序需要类似的凭据文件。

这个问题肯定已经有通用的解决方案了?比如 ssh-agent,它会要求用户输入密码,然后将密码解密到内存中一段时间​​。但不一定像 ssh-agent 那样花哨……代理不需要执行所有加密操作,这可能因应用程序、API 或协议而异。在我看来,只需将凭证文件解密到内存中就很有价值了。

—+ TL;DR – 你可以在这里停下来,不用读剩下的内容

有些人会从上面的“简要”部分明白我的要求。

其他人可能不是。

—+ 这个问题肯定有一个通用的解决方案吧?

肯定存在类似 ssh-agent 的东西,它可以从加密文件中读取这些秘密,要求用户输入密码(或更好的密码),解密这些秘密,并将它们在内存中保存一段时间,这样你就不必不断地重新输入密码等等?

不必像 ssh-agent 那样走得太远,因为代理会执行所有或大部分加密操作 – 因此 ssh 客户端和 ssh-agent 之间的协议不仅仅是“给我凭证”,还必须描述要执行的操作。由于有许多不同的协议,它们具有许多不同的凭证和许多略有不同的操作,因此立即为每个协议创建自定义代理可能会有障碍。但仅仅让持久代理询问用户,然后将凭证从磁盘解密到内存中,总比什么都没有要好。

这肯定已经完成了,并且能够与许多不同的应用程序 XYZ 协同工作?

但我肯定不知道有这样的事。

—+ Google 和 ChatGPT 也没有

事实上,我尝试过的任何人工智能助手也没有这种能力 —— 尽管这可能是我没有正确措辞 LLM 提示或谷歌搜索的问题。

就此而言,ChatGPT 建议我执行以下操作:

  • 加密磁盘上的凭证文件
  • 当我想使用它时

    • 在磁盘上临时创建未加密的凭证文件
    • 让客户端程序(如 gmi/lieer)在运行时访问未加密的凭证文件
    • 当我不再运行客户端时,删除未加密的临时凭证文件

我希望我不需要解释这有多么不令人满意。

—+ 可以使用 UNIX 域套接字或 FUSE 完成此操作吗?已经完成了吗?

如果我知道客户端应用程序始终在读取或替换整个凭证文件,我可以想象让 XYZ 代理一次性将未加密的秘密写入套接字。或者如果我不知道访问模式,例如如果秘密足够大而寻求执行随机访问,我可以想象可以使用像 FUSE 这样的用户域文件系统。

问:是否有人创建过这样一种通用的“将秘密解密到内存中,因此对于无法处理加密凭证文件的软件来说,它看起来像一个未加密的凭证文件?”。

  • 使用 UNIX 域套接字
  • 或 FUSE
  • 或其他

如果对命名空间的这种改变仅限于父进程及其子进程,那就更好了,就像您可能在 Plan9 或 Brazil 等操作系统中所做的那样,尽管据我所知,现有的 UNIX 系统(如 Linux)并不容易做到这一点。

—+ 详细信息

按照我的习惯,我为我的问题提供了太多背景细节。对于许多对安全有一定了解的人来说,这么多细节是不必要的。但有时我所说的内容可能不太清楚。有时我可能使用了不正确的术语。等等。

因此,我提供所有这些额外的细节,希望能减少误解。

如果你真的知道我的问题的答案,你也许可以停下来而不再阅读其余的内容。

哎呀,我还是得承认:我试图绕过那些愚蠢的回答来回答我的问题。但我以前尝试这样做的时候并不总是成功的。

—+ 激励示例:gmi/lieer 使用未加密的凭证文件访问 gmail

例如 lieer 是一个用于将 Gmail 与本地存储同步的程序,它在文件系统中存储了一个未加密的 Gmail 凭证文件。该文件 .credentials.gmailieer.json 是完全未加密的普通明文。

摘录:

gmi init 现在将打开您的浏览器并请求对您的电子邮件进行有限的访问。… 访问令牌存储在本地邮件存储库中的 .credentials.gmailieer.json 中。如果您愿意,您可以指定应使用的自己的 api 密钥。

当然,文件系统权限应该使其只能由我的 UNIX 登录 ID 访问。gmi/lieer 程序使用它来访问我的 gmail 帐户。但除非我完全错过了什么,否则以我的身份运行的任何程序都可以访问此文件。例如,Web 浏览器中无数的沙箱逃逸之一可能允许它访问此文件。或者我可能错误地设置了文件系统权限。或者我可能错误地配置了文件系统/磁盘加密,并且我机器上的其他用户 ID 可能能够访问它。等等。

我认为,出于安全考虑,纯文本机密永远不应存储在磁盘上,这是标准/最佳实践。长期以来,我一直对许多软件系统要求将 API 密钥等凭据存储在磁盘上感到有些惊讶。我通常避免使用这样的系统,尽管它会妨碍需要此类 API 密钥的 Google API 开发等工作。或者我可能会使用这样的系统或工作,但拒绝将它们用于个人事务。然而,我确实想将这样的系统用于个人事务。不仅仅是个人软件开发,而且对于 gmi/lieer 访问我的 Gmail 帐户,这是我能获得的最私人的东西,对我来说比 GitHub 项目敏感得多。

这不仅仅是 gmi/lieer 的问题。许多程序、许多软件系统都要求您将 API 密钥等凭据存储在磁盘上。我认为我还没有遇到过任何将凭据加密存储在磁盘上的程序。

当然,ssh/ssh-agent 和 gpg/gpg-agent 除外,其中凭证文件受到保护,不仅受文件系统权限保护,还受密码保护,并且只能在 ssh-agent 的进程内存中解密。

—+ ssh-agent => 磁盘上没有纯文本凭证

当然,ssh/ssh-agent 和 gpg/gpg-agent 除外。

  • 关键文件受到保护的位置

    • 不仅受文件系统权限保护,而且还受密码加密。

      • 当你将 ID 加载到 ssh-agent 中时

        • 它会要求你输入密码,
        • 读取加密的密钥文件,
        • 并将它们解密到其进程内存中。
        • ssh-agent 是持久性的,所以你只需要偶尔这样做
      • ssh(如果配置正确)

        • 如果不要求 ssh-agent “执行操作”,就无法运行。
        • 通过 UNIX 域套接字与 ssh-agent 通信
        • ssh-agent 实际上完成了所有或大部分公钥计算
        • => ssg 本身没有私钥

我认为这是安全的标准/最佳实践

  1. 纯文本机密绝不应存储在磁盘上

    • 交换文件可能除外,

      • 但这应该是一个已经解决的问题
    • 因此,即使有人可以访问磁盘上的原始数据,你也应该是安全的

      • 例如如果你没有完整的磁盘或文件系统加密
      • 磁盘驱动器在其“主”操作系统之外被访问
  2. 明文私钥可能存储在 ssh-agent 的进程内存中

    • 不在 ssh 客户端程序中
    • 并且不应被任何其他程序访问,

      • 即使以同一用户在同一台​​机器上运行
      • 可能还不能由 root 或 admin 等特权用户使用

        • 可能的例外是调试器
        • 但这也应该是一个已解决的问题(尽管根据我的经验,还没有解决那么多)

—+ 超越 ssh-agent……

跳过此部分,对于我的问题来说,这其实不是必需的,但它可以帮助我在脑海中整理问题。此外,如果有人能告诉我下面这些项目 (3) 和 (4) 的使用范围更广,而且我目前知道,我很乐意听听。

据我所知,第 (1) 和 (2) 项是最先进的,或者至少是实践。但它们留下了一些硬件/逻辑分析仪安全漏洞,这些漏洞已被某些学术和行业项目解决,但据我所知,这些漏洞并不常见:

  1. 在大多数当今系统中,明文秘密可能存储在 DRAM 中

    • 除非程序员非常小心地将它们仅保存在寄存器中

      • 并控制上下文切换,可能会将寄存器保存到内存中
    • 但各种硬件内存加密提案和产品甚至阻止了这种情况的发生

      • 例如,数据可能以未加密的形式存储在缓存中,但可能在缓存和 DRAM 之间加密。
  2. 同样,各种提案和产品都确保您可以连接逻辑分析仪的总线和连接等上的所有流量都经过加密。

  • 2.5:我实际上对 ssh 客户端/代理通信通过 UNIX 域套接字进行感到有点不舒服

    • 据我所知,任何以适当用户 ID 运行的进程都可以访问该套接字,并可以让 ssh-agent 执行操作
    • 据我所知,UNIX 域套接字仅受文件系统权限保护

      • 据我所知,ssh-agent 和 ssh 程序不通过加密通道进行通信
    • 尽管 UNIX 域套接字可能具有一定的随机性,但这一事实降低了暴露风险。而且我知道,有些操作系统(据我所知,不是标准 LINUX)不仅允许通过用户 ID 来限制权限,还允许通过可执行文件 ID 或进程树中的位置来限制权限。
    • 您当然可以使用 JNIX 用户 ID 来实现这一点,但据我所知,这并不常见。

—++ 示例:包含 gmi/lieer 凭证的 gmail 凭证的纯文本文件

gmi/lieer 是一个将 Gmail 与本地存储同步的程序,它将未加密的 Gmail 凭证文件存储在文件系统中

摘录:

gmi init 现在将打开您的浏览器并请求对您的电子邮件进行有限的访问。… 访问令牌存储在本地邮件存储库中的 .credentials.gmailieer.json 中。如果您愿意,您可以指定应使用的自己的 api 密钥。

凭证存储的内容如下所示。我希望我已经删除了所有敏感内容。“胡言乱语”当然是看起来像随机字母和数字,偶尔带有标点符号的东西,这些是与凭证相关的内容。

{"access_token": "xyzzy ~200 bytes of gibberish",
 "client_id": "~40 bytes of gibberish.apps.googleusercontent.com",
 "client_secret": "~20 bytes of gibberish",
 "refresh_token": "~100 bytes of gibberish",
 "token_expiry": "2024-09-15T07:16:24Z",
 "token_uri": "https://accounts.google.com/o/oauth2/token",
 "user_agent": "Lieer",
 "revoke_uri": "https://oauth2.googleapis.com/revoke",
 "id_token": null,
 "id_token_jwt": null,
 "token_response": {"access_token": "~200 bytes of gibberish",
 "expires_in": 3599,
 "scope": "https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/gmail.labels",
 "token_type": "Bearer"},
 "scopes": ["https://www.googleapis.com/auth/gmail.labels",
 "https://www.googleapis.com/auth/gmail.readonly",
 "https://www.googleapis.com/auth/gmail.modify"],
 "token_info_uri": "https://oauth2.googleapis.com/tokeninfo",
 "invalid": false,
 "_class": "OAuth2Credentials",
 "_module": "oauth2client.client"
}

这是完全纯文本,尽管当然只有我的 Linux 用户 ID 才能访问。

它由 gmi 程序用来验证 gmail。如果不存在,我就无法访问我的 gmail。系统不会要求我输入密码等。

除非我遗漏了什么,否则这个凭证可以允许几乎任何可以读取此文件的程序访问我的 Gmail。这让我很担心。

—++ 不仅仅是 gmi/lieer——还有很多程序

我不打算列出更多示例。但只要谷歌搜索 API KEY 就能找到很多。

—++ 它只是过时的遗留软件吗?

有可能,但恕我直言,并非完全如此。

例如,gmi/lieer 源代码和/或文档表明它使用的是旧的 Gmail API,应该升级。也许更新的 API 可以解决这个问题 – 但据我所知还不行。也许已经有一个通用的 OpenAuth 代理 – 但我找不到。据我所知,谷歌确实更喜欢将 OpenAuth 内容保留在自己的库中,供谷歌 Chrome 和其他网络浏览器使用,并且并没有真正为支持命令行或其他非浏览器实用程序做太多工作。他们真的希望你不要使用这样的实用程序,除非谷歌编写了它们。他们只是勉强支持这样的实用程序,允许你获取 API 密钥等。如果将此类凭据以未加密的形式存储在磁盘上会导致安全漏洞,他们只会将其作为更多证据来证明锁定某些东西并将其其他软件拒之门外是合理的。

无论如何:如果有一个通用的 OpenAuth 代理(可能不这么叫)——我很乐意听到它。

但无论如何,此外:即使有通用的 OpenAuth 代理,也有很多现有程序假设磁盘上有未加密的信用文件。在它们可以升级之前,为这些程序提供通用解决方案是有价值的。假设它们可以升级。

7

  • 我强调这是一个一般性问题,不只是与 gmi/lieer 有关。如果有一个适用于任何需要未加密凭证文件的程序的解决方案,那就太好了。但 gmi/lieer 解决方案总比没有好。我应该在哪里发布这样的问题? 有几个 lieer 线程,还有。但没有一个是关于凭证的。据我所知,不鼓励交叉发布。就此而言,OpenAuth 可能也有一个不完全通用的解决方案。当然,这是一个安全问题。


    – 


  • 你能以问题的形式提出这个问题吗?你能创建一个加密文件系统来存储你的凭证,并控制哪些进程可以挂载解密覆盖吗?


    – 

  • 我认为这应该很容易用命名管道来实现?通常可以在进程需要文件的所有地方提供命名管道。当从命名管道读取任何内容时,您可以编写任何您想要的逻辑。一种可能的解决方案是常驻进程,可以通过命令访问它以提供解密密码。如果存在,则从命名管道读取将即时解密数据,如果不存在,则只会出错并显示一条消息“请在访问此文件前提供密码”


    – 

  • 我不知道 unix 进程是否也可以在读取时阻止并提示输入密码。它需要将当前正在运行的进程 (gmail) 置于后台并捕获 STDIN 以读取密码,然后将原始进程 (gmail) 放回前台。


    – 

  • 查看有关 EncFS 的,您也可以将其应用于其他加密方法;如果程序保持未加密的文件打开,那么您可以延迟卸载解密的文件系统,并且将来的进程将无法访问该文件,但程序可以继续使用它。


    – 


最佳答案
2

您已经就此尝试了很多可能的路径,并对问题空间进行了大量调查;深入研究(并对该问题进行投票)。

但我想你已经知道答案了;没有通用的解决方案。像 ssh-agent(或 gpg-agent)这样的工具需要编写软件才能与它们一起工作。如果软件被编写为只读取磁盘文件,那么它们将永远不会与这些代理通信。

并且意识到这些代理在多用户环境中并不安全;您机器上的根可以访问存储在代理中的凭据。

其他工具(例如 rclone)可以加密其配置文件并要求您输入密码才能使用它们。

即使在 Apple 设备上,也需要编写应用程序来与钥匙串对话以安全地存储凭据。

那么我们可以解决这个问题吗?这取决于你的偏执程度(和软件)。

例如,您可以创建一个加密文件系统。它不需要是一个分区;它可以是您在现有文件系统中创建并挂载的文件。请参阅luks和相关工具。这将保护您的数据免遭他人窃取您的磁盘,因为它要求您输入密码才能解密磁盘。然后您可以将配置文件符号链接或绑定挂载到此文件系统中。

和以前一样。这不会保护您免受 root 用户或恶意软件的攻击,因为一旦安装文件,它们就可以读取。但它确实可以防止盗窃(例如丢失笔记本电脑)。不过,如果您担心这一点,您可能需要考虑加密整个文件系统。如果所有内容都加密了,那么您的配置文件也会加密!

如果您希望每隔一段时间就要求重新认证一次(“哦,距离我上次输入密码已经过去了一个小时”),那么事情就会变得更加困难。我不知道有任何“开箱即用”的解决方案。我会考虑编写一个可以挂载的 FUSE 文件系统,并且每小时需要一次密码提示才能继续解密。

加密文件系统和适当的文件系统访问控制措施。除非您编写了应用程序来确保整个路径上的凭据安全,否则您无法在不影响整个系统的情况下获得更好的效果。

加密文件系统可确保数据在静止时加密,并且只有受信任的操作系统功能才能访问数据。我们无法做得更好。由于交换,任何未明确标记为保存在内存中的数据都可能泄漏到磁盘。

适当的文件访问控制机制(无论是 ACL、专用用户还是其他机制)都可以确保只有相关程序(和 root)才被允许从操作系统访问文件。如果您认为操作系统和 root 是可信的(这是必要的先决条件),那么这将保护您免受任何其他来源访问该数据的侵害。