服务器/数据库配置文件,包括密码,是不是应该存储在源代码控制中?

Posted

技术标签:

【中文标题】服务器/数据库配置文件,包括密码,是不是应该存储在源代码控制中?【英文标题】:Should server/database config files, including passwords, be stored in source control?服务器/数据库配置文件,包括密码,是否应该存储在源代码控制中? 【发布时间】:2011-05-13 16:36:35 【问题描述】:

我希望听到一些最佳做法...

假设一个 Web 应用程序与几个不同的生产服务器(数据库等)交互...是否应该将包含数据库密码的配置文件存储在源代码控制中(例如 git、svn)?

如果没有,跟踪您的应用程序需要访问的服务器数据库(或其他相关)密码的最佳方法是什么?

编辑:添加了赏金以鼓励更多讨论并听取更多人认为的最佳做法。

【问题讨论】:

数据库认证是幼稚的。在安全锁定中,不需要数据库密码。 【参考方案1】:

撇开密码不应该以纯文本任何地方(除了某人的颅骨或只有 CEO、CFO 和 CIO 才能访问的锁定保险库(并且同时需要所有三个密钥) )),您应该将构建您的产品所需的所有内容都存储到源代码管理中。

这不仅意味着您的源代码,还包括构建机器的规范、编译器选项、编译器本身等等。

如果我们能找到检查物理硬件的方法,我们也会这样做:-)

构建过程本身可以复制的所有内容,或运行而不是构建软件的任何内容(例如您的密码)通常不属于源代码控制,但有些商店会这样做他们的可执行文件、生成的文档等,以便他们可以快速获取特定版本进行安装。

【讨论】:

如果“密码永远不应该存储在任何地方”,需要密码的应用程序应该如何维护,例如跨重启?您是否声称唯一可接受的情况是人类每次需要时都必须输入密码? @Kenny,我的错,我的意思是纯文本。我的意思是,坏人应该没有办法获得明文密码。这意味着要么仅将其存储在他们无法获取的地方,要么将其加密存储在他们可以获取的地方,但确保他们无法获取解密它的代码。但是该评论本身并不是答案的一部分,因此我认为您在那里关注了错误的事情。但是,你是对的,所以我会在答案中澄清它。【参考方案2】:

我更喜欢在主 settings 文件旁边有一个 local_settings 文件。这个 local_settings 不应该添加到存储库中,但我会在存储库中添加一个 sample.local_setting 以显示此文件的结构。

在运行时,如果 local_settings 存在,其值将覆盖主设置文件的值。

例如在python中:

settings.py:

log='error.log'
db=lambda:None
db.host='localhost'
db.user=''
db.password=''

try:
    import local_settings
except ImportError:
    pass

local_settings.py:

from settings import *

db.user='abcd'
db.password='1234'

【讨论】:

【参考方案3】:

如果没有正确的构建过程,我正在使用这个策略(对于 php 应用程序):

    创建一个文件夹/etc/companyname

    在里面放两个文件:

    <?php // env.php
    return 'prod'; 
    
    <?php // appname-prod.php
    return array(
      'db' => array( /* credentials */ ),
      /* other host-specific conf data */
    ); 
    

    使这两个文件只能由您的 PHP 进程读取

现在您应用的配置文件将类似于:

<?php // config.php
$env = (require "/etc/companyname/env.php");
$creds = (require "/etc/companyname/appname-$env.php");

有了这个,环境定义了使用的凭据,您可以在预配置的环境之间移动代码(并使用$env 控制一些选项)。当然,这可以通过服务器环境变量来完成,但是这 a) 设置起来更简单 b) 不会向服务器上的每个脚本公开凭据(不会出现在像 phpinfo() 这样的杂散调试垃圾中)。

为了在 PHP 之外更容易阅读,您可以将凭证文件设为 JSON 或其他内容,然后忍受微小的性能损失(APC 不会缓存它们)。

【讨论】:

【参考方案4】:

源代码中的密码问题:

很难从一个部署到另一个部署(我不想在生产中修改源代码) 在进行开发时增加了意外损坏生产数据库的可能性 安全问题(在大多数商店中,代码/开发人员没有理由知道产品密码) 更改的密码需要重新部署

我发现最好的方法是检查一个配置,该配置使用混合默认值和占位符来存储特定于部署的数据。我们的应用程序总是寻找允许覆盖任何变量的系统配置。这允许生产机器拥有适合其部署的配置。

注意:当我担任管理员时,我总是将配置与代码分开管理(有充分的理由)。

【讨论】:

【参考方案5】:

密码不应存储在源代码管理中。完全没有。曾经。见How to keep secrets secret

密码、服务器名称等是服务器管理员执行的部署配置的一部分。必须记录此程序并将记录的程序置于控制之下。

或者,部署配置可以由系统管理员运行以执行配置的脚本来执行,并且在脚本执行期间它会要求系统管理员提供所需的信息。同样,此脚本必须保存在版本控制中。

除服务器配置之外的所有其他内容必须在源代码管理中。

在源代码管理中存储服务器配置通常是一个坏主意,因为它会妨碍部署并可能导致小灾难(例如,当有人没有意识到他们从源代码管理部署的测试版本正在与实时服务通信时) .

始终将这些配置文件保存在 webroot 之外。

可信连接可能是一个选项,允许已知 IP 地址通过该服务的配置连接到服务..

在 Windows 上运行时使用集成身份验证。参考Securing Data Access mysql 配置为允许来自本地主机的连接并且不需要密码。见Step 7: Securing a MySQL Server on Windows PostgreSQL 你可以使用~/.pgpass。

【讨论】:

您的前两段似乎相互矛盾...您能澄清一下吗? 这是部署过程,应该在源代码控制中,这应该记录在哪里放置密码,或者如果它是一个脚本,则询问密码/服务器名。 “这个文档/脚本应该在源代码管理中,并且应该要求输入密码/服务器名”你能解释一下你的意思吗? 如果您要记住 100 个密码怎么办?处理部署的人应该记住所有这些吗?如果他们输入错误的密码并且应用程序无法连接到数据库会发生什么?这似乎不是一个非常可靠的方法.. 您意识到将列表保存在某处的系统管理员并不比将密码存储在源代码管理中更安全,对吧?【参考方案6】:

我总是排除包含密码或其他访问详细信息(如数据库)的重要配置文件,这纯粹是最佳实践。此外,源代码和版本控制通常为多个用户提供服务,并且并非所有用户都使用相同的数据库详细信息,甚至使用相同的服务器配置(域等),为此,配置文件应保持排除在他之外一大堆。

【讨论】:

【参考方案7】:

示例配置文件,当然,我会将它们置于版本控制之下。但通常不会使用真实世界的访问数据,例如服务器地址或密码。更多类似的东西

# 程序.conf # # $myprog 的 mysql 选项。 # #SERVER_ADDR=127.0.0.1 #SERVER_USER=mysql #SERVER_PASSWD=abcdef

【讨论】:

【参考方案8】:

这里没有单一的“灵丹妙药”答案,这在很大程度上取决于细节。

首先,我认为最佳做法是将所有源代码与单独存储库中的配置分开。因此,源代码仍然是源代码,但它的安装或部署(使用配置、密码等)是另一回事。这样,您就可以将开发人员的任务与系统管理员的任务牢牢分开,并最终建立两个不同的团队来做他们擅长的事情。

当您拥有单独的源代码存储库 + 部署存储库时,您最好的下一个选择是考虑部署选项。我在这里看到的最佳方式是使用所选操作系统的典型部署过程(即按照操作系统维护者的方式为所选操作系统构建自主包)。

例如,Red Hat 或 Debian 打包程序通常意味着从外部站点获取一个 tarball 软件(这将是从您的源代码 VCS 导出源代码)、解包、编译和准备包以准备部署。理想情况下,部署本身应该意味着只需执行一个快速简单的命令来安装软件包,例如rpm -U package.rpmdpkg --install package.debapt-get dist-upgrade(假设您构建的软件包会转到 apt-get 能够找到的存储库他们)。

显然,要使其以这种方式工作,您必须为处于完全工作状态的系统的所有组件提供所有配置文件,包括所有地址和凭据。

为了更简洁,让我们考虑一个典型的“小服务”情况:一个 PHP 应用程序部署在 n 个运行 apache/mod_php 的应用程序服务器上,访问 m 个 MySQL 服务器。所有这些服务器(或虚拟容器,这并不重要)都驻留在受保护的专用网络中。为了让这个例子更简单,我们假设所有真正的互联网连接都由一组 k http 加速器/反向代理(例如 nginx / lighttpd / apache)组成,它们的配置非常简单(只是内部 IP转发到)。

我们需要什么才能让他们相互联系并充分发挥作用?

MySQL 服务器:设置 IP/主机名、设置数据库、提供登录名和密码 PHP 应用程序:设置 IP/主机名,创建将提及 MySQL 服务器 IP、登录名、密码和数据库的配置文件

请注意,这里有 2 种不同“类型”的信息:IP/主机名是固定的,您可能希望一劳永逸地分配它们。另一方面,登录名和密码(甚至数据库名称)在这里纯粹是为了连接目的 - 以确保 MySQL 确实是我们的 PHP 应用程序连接到它。所以,我在这里的建议是拆分这两种“类型”:

“永久”信息,例如 IP,应存储在某些 VCS 中(不同于源代码 VCS) “临时”信息(例如 2 个应用程序之间的密码)不应存储,而应在生成部署包时生成。

最后一个也是最棘手的问题仍然存在:如何创建部署包?有多种技术可用,主要有两种方法:

从 VCS1 导出的源代码 + 从 VCS2 导出的“永久”配置 + 从 VCS3 构建脚本 = 包 源代码在VCS1中; VCS2 是一个分布式版本控制(如 git 或 hg),它本质上包含 VCS1 + 配置信息 + 构建脚本的“分支”,可以生成 .我个人更喜欢这种方法,它更短,最终更易于使用,但学习曲线可能有点陡峭,尤其是对于必须掌握 git 或 hg 的管理员来说。

对于上面的示例,我将创建如下包:

my-application-php - 这将取决于 mod_php、apache 并将包括像 /etc/my-php-application/config.inc.php 这样的生成文件,其中将包括 MySQL 数据库 IP/主机名和生成为 md5(current source code revision + salt) 的登录名/密码。该软件包将安装在每 n 个应用程序服务器上。理想情况下,它应该能够安装在干净安装的操作系统上,并在无需任何手动操作的情况下构建一个完全正常工作的应用程序集群节点。 my-application-mysql - 这将取决于 MySQL-server 并包含以下安装后脚本: 启动 MySQL 服务器并确保它会在操作系统启动时自动启动 连接到 MySQL 服务器 检查所需数据库是否存在 如果否 - 创建数据库,使用内容引导它并使用密码创建登录名(与/etc/my-php-application/config.inc.php 中生成的登录名和密码相同,使用 md5 算法) 如果是 - 连接到数据库,应用迁移以将其升级到新版本,终止所有旧登录名/密码并重新创建新的登录名/密码对(同样,使用 md5(revision + salt) 方法生成)

最终,它应该会带来使用 generate-packages &amp;&amp; ssh-all apt-get dist-upgrade 之类的单个命令升级部署的好处。此外,您不会将跨应用程序密码存储在任何地方,并且它们会在每次更新时重新生成。

这个相当简单的示例说明了您可以在此处使用的许多方法 - 但是,最终由您来决定哪种解决方案在这里更好,哪种解决方案过于矫枉过正。如果您在此处或作为单独的问题提供更多详细信息,我很乐意尝试详细说明。

【讨论】:

虽然可能没有“银弹”,但我认为有一些不好的做法。您可以决定采用不良做法,因为您觉得权衡对您有利,但这种做法仍然很糟糕。【参考方案9】:

在我的用于 PHP 的 Subversion 存储库中,包含密码的配置文件以 config.php.sample 签入,并提示必须提供的内容,并且依赖的脚本要求 config.php 出现在同一位置。

存储库配置为忽略该目录的config.php,以避免“意外”添加或签入。

【讨论】:

【参考方案10】:

我认为这个问题更多地是关于信息所有权、信任和组织。您应该扪心自问,您会信任组织的哪一部分来保护您的系统密码不被泄露和滥用?

我曾在由负责业务的人员保管的组织中工作。在其他情况下,他们被委派给运营团队,该团队也负责创建和使用等流程。

最重要的是,在您的组织中明确定义了谁应该有权访问系统密码。之后,您可以决定保护密码的适当技术解决方案。

【讨论】:

【参考方案11】:

总的来说,我同意 paxdiablo:将所有可能的内容置于源代码控制之下。这包括带有数据库凭据的生产配置文件。

想想你的服务器崩溃的情况,备份结果很糟糕,你需要让服务器恢复。我认为您和您的客户(或老板)肯定会同意,拥有在源代码管理中部署站点所需的一切是一大优势。

如果您想使用持续集成(另一种最佳实践)从源代码构建易于部署的软件包,您必须将配置文件置于源代码控制之下。

还要考虑到,在大多数情况下,拥有源代码控制访问权限的开发人员无法直接访问生产数据库服务器。生产密码对他们毫无用处。

如果错误的人访问了您的资源,他们仍然需要访问生产服务器才能破坏密码。所以,如果你的生产环境得到适当的保护,源代码管理中密码的安全风险是非常有限的。

【讨论】:

-1 绝对不是生产数据库凭据,至少不是密码。 @JeremyP 我们在这里讨论的是什么类型的应用程序?对于高知名度的网站,我会说你保持谨慎是正确的。对于不太重要的应用程序和小型团队,我认为将密码存储在源代码管理中是完全可以的。 @Marnix van Valen:我不同意密码是配置的一部分。密码用于验证“用户”。如果每个人都能看到它,它就违背了拥有它的目的。密码也需要在短时间内更改,以防被泄露。 @JeremyP 如果密码存储在配置文件中,那么我会说它是配置的一部分。可见性仅限于有权访问源存储库的人员。如果您不信任您的开发人员,请不要让他们直接(网络)访问生产数据库。如果没有访问数据库,密码一开始是没有用的。密码仍然可以在需要时更改,只需在之后签入源代码管理即可。这甚至可能比打开保险库并更新那张密码表要少。 @Marnix van Valen:但是您必须将对源代码存储库的读取权限限制为允许查看生产数据库内容的人员。您必须进行更改控制才能更改密码。如果您忘记更改存储库中的密码,下一次部署会破坏一切。我建议我们应该同意在这一点上有所不同,然后就这样吧。【参考方案12】:

没有。生产密码应直接在服务器上配置。您应该为部署团队/人员创建部署说明,以便在部署期间更改正确的属性文件。

【讨论】:

那么您会将这些部署说明放在哪里?在源代码管理中? 我以前使用过的一种方法是将密码存储为部署期间点源的局部变量(在 powershell 脚本中)。这些文件已被管理员锁定。这样做的好处是您仍然可以实现自动化,同时将密码保存在安全的位置。

以上是关于服务器/数据库配置文件,包括密码,是不是应该存储在源代码控制中?的主要内容,如果未能解决你的问题,请参考以下文章

存储网络服务的密码?

mysql服务不能启动

ssh配置免密后依然需要输入密码的问题解决及排查过程

存储密码的位置 - 第 3 方 API

phpMyAdmin 尝试连接到 MySQL 服务器,但服务器拒绝连接。您应该检查配置文件中的主机用户名和密码

在会话中存储密码是不是安全? [复制]