如何在PHP中保护数据库密码?


404

当PHP应用程序建立数据库连接时,通常当然需要传递登录名和密码。如果我为我的应用程序使用单个最小权限登录名,则PHP需要在某个地方知道该登录名和密码。保护该密码的最佳方法是什么?似乎只在PHP代码中编写它不是一个好主意。


1
为了完全安全,您需要建立ssl连接,否则网络上的任何人仍然可以嗅探您输入的密码。
Charles Ma

1
您是指连接字符串中使用的用户密码还是数据库密码?
Ozgur Ozcitak

4
连接字符串中使用的数据库密码。谢谢!
user18359

Answers:


238

有人将其误解为关于如何在数据库中存储密码的问题。那是错的。它是关于如何存储使您能够访问数据库的密码的。

通常的解决方案是将密码从源代码移到配置文件中。然后,由系统管理员来管理并保护该配置文件。这样,开发人员无需了解任何有关生产密码的信息,并且在源代码管理中也没有密码的记录。


8
谢谢。如果我正确理解这一点,则php文件将在配置文件中包含一个include,从而允许其使用密码。例如,我创建了一个名为“ app1_db_cfg.php”的文件,其中存储了登录名,密码和数据库名称。然后,我的application.php页面包含“ app1_db_cfg.php”,并且我已经营业了!
user18359

28
我同意该配置需要得到适当的保护。但是,知道如何做到这一点是系统管理员而不是开发人员的责任。在这种情况下,我不同意强加密的价值。如果您不能保护您的配置文件,是什么让您认为您可以保护自己的钥匙?
user11318

10
我更喜欢使用仅允许从Web服务器访问数据库的数据库帐户。然后,我不必麻烦加密配置,只需将其存储在Web根目录之外即可。
gnud

11
我使用apache环境变量来设置路径,以便在源代码中甚至文件的路径也是未知的。这也很好地允许基于serverm上的Apache设置使用不同的密码进行开发和生产
geedew 2014年

15
请记住,即使存储在Web可访问目录之外的文件也必须由使用它们的脚本读取。如果有人包括该文件,然后转储该文件中的数据,他们将看到密码。
里克·麦克·吉利斯

104

如果您托管在其他人的服务器上,并且没有在Webroot之外的访问权限,则始终可以将密码和/或数据库连接放入文件中,然后使用.htaccess锁定该文件:

<files mypasswdfile>
order allow,deny
deny from all
</files>

3
谢谢,这正是我想要的。
David Gladfelter,2009年

28
绝对可以,但是如果有人可以使用Shell进行访问,则您的整个帐户都会受到威胁。
凯伦2010年

4
这是一种不好的做法,因为您可能不小心将凭据提交到存储库。
Porlune

2
@Ankit:如果非友好用户有可能将文件上传到服务器并执行该文件,则服务器配置不正确。
凯伦

6
@Porlune:开发人员应使其版本控制系统忽略密码文件,即使用.gitignore。但是,是的,应谨慎处理包含敏感数据的文件。
凯伦

45

最安全的方法是完全不使用PHP代码中指定的信息。

如果使用Apache,则意味着在httpd.conf或虚拟主机文件文件中设置连接详细信息。如果这样做,则可以不带任何参数调用mysql_connect(),这意味着PHP将永远不会输出您的信息。

这是您在那些文件中指定这些值的方式:

php_value mysql.default.user      myusername
php_value mysql.default.password  mypassword
php_value mysql.default.host      server

然后您像这样打开mysql连接:

<?php
$db = mysqli_connect();

或像这样:

<?php
$db = mysqli_connect(ini_get("mysql.default.user"),
                     ini_get("mysql.default.password"),
                     ini_get("mysql.default.host"));

1
请检查的ini_get(“默认值”),正确的价值观php.net/manual/en/class.mysqli.php
瓦尔

4
是的,但是任何用户(或滥用php脚本错误的黑客)都可以通过读取密码ini_get()
Marki555

1
@ Marki555 but any user (or a hacker abusing badly written php script) can read the password via ini_get()您如何处理?
tonix

2
Marki555表示,可以运行PHP代码的攻击者也可以调用PHP函数,这显然是正确的,而且无法采取任何措施。我还想补充一点,我不再遵循我自己在此答案中给出的建议,而是使用环境变量。不过,概念很相似:不要将您的凭据存储在代码中,而是以某种方式注入它们。使用ini_get()或并不重要getenv()
拉尔斯·尼斯特罗姆(LarsNyström)

2
@DeepBlue如果可以注入ini_get(),也可以注入file_get_contents(anypath)。只要php有办法获取密码,任何恶意代码也会如此。
varesa

40

将它们存储在Web根目录之外的文件中。


29
而且,正如其他地方所提到的,在源代码控制之外。
Frank Farmer

6
我们可以包括吗?例如在PHP中我们可以做include('../otherDirectory/configfile.conf')吗?
mtk

1
大家都建议将凭据存储在wwwroot之外。好的,我了解安全背景。但是,然后应如何将其存储在版本控制中(示例配置)?通常wwwroot是git repo的根,因此,如果外部没有任何内容-它将在VC之外。想象一下,新的开发人员试图建立一个本地实例进行开发-他应该如何理解“获取此文件,将其复制到外部并填写”之类的魔术?
教父

@TheGodfather的想法是,新开发人员应对自己的开发环境拥有自己的凭据。尽管在代码中包含说明或配置指示或注释的自述文件是一种好习惯,但不要配置实际数据。
PhoneixS

@TheGodfather,自述文件?
起搏器

35

对于非常安全的系统,我们将数据库密码加密在一个配置文件中(该密码本身由系统管理员保护)。在应用程序/服务器启动时,应用程序然后提示系统管理员输入解密密钥。然后从配置文件中读取数据库密码,解密并存储在内存中以备将来使用。由于它存储在已解密的内存中,因此仍不是100%安全的,但是您有时必须称其为“足够安全”!


85
如果管理员去世怎么办?
Radu Murzea

36
@RaduMurzea太荒谬了。您何时听说Sys Admins快要死了?它们就像麦当劳,它们只是突然出现/消失!
ILikeTacos

13
@Radu Murzea拥有2个或更多管理员,那么您具有像RAID阵列一样的奇偶校验。一次出现多个驱动器故障的可能性要低得多。
element11 2015年

5
服务器重新启动怎么办?叫醒管理员让他们在..etc.etc中键入密码所需的时间如何?大声笑
约翰·亨特

1
不知道“存储在内存中”是什么意思。通常,PHP Web应用程序在内存中存储任何内容的时间都不会比响应单个请求查看页面所花费的时间更长。
bdsl

15

该解决方案是通用的,因为它对开放源代码应用程序和封闭源代码应用程序都很有用。

  1. 为您的应用程序创建一个OS用户。参见http://en.wikipedia.org/wiki/Principle_of_least_privilege
  2. 使用密码为该用户创建(非会话)OS环境变量
  3. 以该用户身份运行该应用程序

优点:

  1. 您不会偶然将密码签入源代码管理中,因为您不能
  2. 您不会意外搞乱文件权限。好吧,也许可以,但这不会影响到这一点。
  3. 只能由root或该用户读取。Root仍然可以读取您的所有文件和加密密钥。
  4. 如果使用加密,如何安全地存储密钥?
  5. 适用于x平台
  6. 确保不要将envvar传递给不受信任的子进程

此方法由非常成功的Heroku建议。


11

如果可以在存储凭据的同一文件中创建数据库连接。内联connect语句中的凭据。

mysql_connect("localhost", "me", "mypass");

否则,最好在connect语句之后取消设置凭据,因为不能从内存中读取不在内存中的凭据;)

include("/outside-webroot/db_settings.php");  
mysql_connect("localhost", $db_user, $db_pass);  
unset ($db_user, $db_pass);  

9
如果有人可以访问内存,那么无论如何您都会被搞砸。这是毫无意义的伪造安全性。唯一安全的选择是在网络根目录之外(或者,如果您没有在网络根目录之上的访问权限,则至少受.htaccess保护)。
uliwitness 2012年

2
@uliwitness-就像是在说有人可以用乙炔火炬切穿您的网络运营中心的锁,这意味着该门也是伪造的安全性。将敏感信息绑定到尽可能小的范围总是很有意义的。
路加·A·勒伯

1
回显$ db_user或打印$ db_pass怎么样?即使是同一个团队中的开发人员,也应该无法弄清生产凭证。该代码不应包含有关登录信息的任何可打印内容。
Mohammed Joraid

8

正如您所说的那样,您的选择是有限的,因为您需要密码才能访问数据库。一种通用方法是将用户名和密码存储在单独的配置文件中,而不是主脚本中。然后确保将其存储在主Web树之外。就是说,如果有一个网络配置问题,使您的php文件仅以文本形式显示而不是被执行,则您没有暴露密码。

除此之外,您位于右边,对正在使用的帐户的访问权限最少。加上

  • 不要将用户名/密码的组合用于其他任何用途
  • 将数据库服务器配置为仅接受来自该用户的Web主机的连接(如果数据库位于同一台计算机上,则localhost会更好)。这样,即使公开了凭据,它们也不会被任何人使用,除非他们有权访问其他主机。机。
  • 混淆密码(即使是ROT13也会混淆),即使有人确实可以访问该文件,也不会提供太多防御,但是至少可以防止随意查看该文件。

彼得


8

如果您使用的是PostgreSQL,则它将~/.pgpass自动查找密码。有关更多信息,请参见手册


8

以前,我们将DB用户/密码存储在配置文件中,但此后一直处于偏执状态-采用“ 深度防御”策略。

如果您的应用程序受到威胁,则用户将拥有对您的配置文件的读取访问权限,因此,破解者有可能读取此信息。配置文件也可能陷入版本控制中,或在服务器周围复制。

我们已经切换到将用户/传递存储在Apache VirtualHost中设置的环境变量中。此配置只能由root用户读取-希望您的Apache用户不是root用户运行。

这样做的缺点是现在密码在Global PHP变量中。

为了减轻这种风险,我们采取以下预防措施:

  • 密码已加密。我们扩展了PDO类,以包括用于解密密码的逻辑。如果有人在我们建立连接的地方读取了代码,则显然不是使用加密密码而不是密码本身来建立连接。
  • 加密的密码从全局变量移至私有变量。应用程序立即执行此操作,以减小该值在全局空间中可用的窗口。
  • phpinfo()被禁用。PHPInfo是一个容易获得的目标,可以全面了解所有内容,包括环境变量。

6

将数据库密码放在文件中,使其对提供文件的用户只读。

除非您有一些只允许php服务器进程访问数据库的方法,否则这几乎是您可以做的。


5

如果您在谈论数据库密码,而不是来自浏览器的密码,则标准做法似乎是将数据库密码放在服务器上的PHP配置文件中。

您只需要确保包含密码的php文件具有适当的权限即可。也就是说,它只能由Web服务器和您的用户帐户读取。


1
不幸的是,PHP配置文件可以由phpinfo()读取,并且如果有人碰巧将一些测试脚本遗忘在幸运的攻击者身后,便可以读取密码。最好将连接密码保留在Web服务器根目录之外的文件中。然后访问它的唯一方法是使用Shell或执行任意代码,但是在这种情况下,所有安全性都会丢失。
MarioVilas 2014年

5

只需将其放入配置文件中的某个位置即可。只要确保您:

  1. 禁止从网络外部的任何服务器访问数据库,
  2. 注意不要意外地向用户显示密码(在错误消息中,或者通过意外地将HTML用作PHP文件等)。

5

我们已经通过以下方式解决了它:

  1. 在服务器上使用内存缓存,并与其他密码服务器建立开放连接。
  2. 将密码(甚至所有已加密的password.php文件)和解密密钥保存到内存缓存中。
  3. 该网站调用保存密码文件密码的memcache密钥,并在内存中解密所有密码。
  4. 密码服务器每5分钟发送一次新的加密密码文件。
  5. 如果在项目上使用加密的password.php,则进行审核,检查该文件是否在外部被触摸或查看过。发生这种情况时,您可以自动清理内存并关闭服务器以进行访问。

4

另一个技巧是使用一个如下所示的PHP单独的配置文件:

<?php exit() ?>

[...]

Plain text data including password

这不会阻止您正确设置访问规则。但是,如果您的网站被黑,“ require”或“ include”将仅在第一行退出脚本,因此更难以获取数据。

但是,永远不要将配置文件放在可以通过Web访问的目录中。您应该有一个“ Web”文件夹,其中包含您的控制器代码,css,图片和js。就这样。脱机文件夹中还有其他内容。


但是,php脚本如何读取存储在文件中的凭据?
Christopher Mahan

3
您可以像常规文本文件一样使用fopen()。
e-satis

2
@ e-satis好的,它将阻止黑客这样做require/ include但是如何防止这样做fopen
dmnc

“这不会阻止您正确设置访问规则”
e-satis

@ e-satis,这非常聪明。想知道为什么没人想到它。 但是,仍然容易受到编辑器复制问题的影响。feross.org/cmsploit
起搏器

4

最好的方法是根本不存储密码!
例如,如果您在Windows系统上并连接到SQL Server,则可以使用集成身份验证使用当前进程的身份,无需密码即可连接到数据库。

如果确实需要使用密码,首先连接加密它,使用强大的加密(例如,使用AES-256,然后保护加密密钥,或使用非对称加密,并有操作系统保护证书),然后将其存储在一个具有强ACL的配置文件(在Web目录外部)。


3
再次加密密码毫无意义。可以获取未加密密码的人也可以获取解密密码所需的密码。但是,使用ACL和.htaccess是一个好主意。
uliwitness 2012年

2
@uliwitness我想您可能会误解了-“ 再次加密” 是什么意思?这只是一种加密。而且,您不想使用密码短语(供人类使用)对其进行加密,而是使用强大的密钥管理(例如,由操作系统保护),而这种方式使得仅访问文件系统不会授予对密钥的访问权限。
AviD 2012年

3
加密不是万能的-与其使用ACL保护AES密钥,不如直接在其中存储密码。访问AES密钥或解密的密码没有什么区别,在这种情况下,加密只是蛇油。
MarioVilas 2014年

@MarioVilas a?如果密码是加密的,并且加密密钥受操作系统保护,那么没有什么区别?加密不是魔术,它只是将所有保密性压缩为较小的加密密钥。在这种情况下,它几乎没有什么用,它只是所有保密性转移到了操作系统中。
2014年

6
@AviD OS如何保护密钥而不保护数据本身?答:它可以同时保护两者,因此加密并没有真正的帮助。如果仅存储数据并且从用户必须输入的密码(例如,密码)派生出加密密钥,那将是不同的。
MarioVilas 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.