我们将所有应用程序和数据库密码以纯文本形式存储在源代码控制中。我们这样做是因为我们的构建/部署过程会生成所需的配置文件,并且还会进行需要这些密码的实际部署(即:对数据库运行 sql 需要您使用有效的凭据登录到数据库)。有没有人有类似的需求,您可以在不以纯文本形式存储密码的情况下实现此类功能?
6 回答
如果您的计划是存储所有代码和配置信息以直接从版本控制运行生产系统,并且无需人工干预,那么您就完蛋了。为什么?这完全违反了旧的安全公理“永远不要写下你的密码”。让我们做一个否定证明。
首先,您在配置文件中有纯文本密码。这不好,任何可以看到文件的人都可以阅读它们。
第二切,我们将加密密码!但现在代码需要知道如何解密密码,所以需要将解密密钥放在代码中的某个位置。这个问题刚刚被推低了一个层次。
使用公钥/私钥怎么样?与密码相同的问题,密钥必须在代码中。
使用未存储在版本控制中的本地配置文件仍然会将密码以及读取密码的方法(如果它们已加密)放在磁盘上并可供攻击者使用。您可以通过确保配置文件的权限非常有限来稍微强化一下,但是如果该框被植根,您就完蛋了。
这让我们明白为什么将密码放在磁盘上是一个坏主意。它违反了安全防火墙的概念。一台包含登录信息的被入侵机器意味着其他机器将被入侵。一台维护不善的机器可能会摧毁您的整个组织。
在某些时候,人类将不得不注入关键秘密来启动信任链。您可以做的是加密代码中的所有秘密,然后在系统启动时让人工手动输入密钥以解密所有密码。这就像 Firefox 使用的主密码系统。它很容易被滥用,因为一旦一个密码被泄露,许多系统可能会被泄露,但它更方便并且可能更安全,因为用户只需要记住一个密码并且不太可能写下来。
最后一点是确保如果登录信息被泄露(并且您应该始终假设它会),A)攻击者无法对其进行太多操作,并且 B)您可以快速关闭被泄露的帐户。前者意味着只为帐户提供所需的访问权限。例如,如果您的程序只需要从数据库中读取数据,则让它登录一个限制为 SELECT 的帐户。通常,删除所有访问权限,然后仅在必要时添加。对删除的权利要小气,以免你得到小鲍比桌的访问。
后者意味着您为每个用户/组织/项目提供自己的登录名,即使他们可以拥有完全相同的权限和特权并访问相同的数据。这有点麻烦,但这意味着如果一个系统受到威胁,您可以快速关闭该帐户,而不会关闭您的整个业务。
我假设目标是您不希望任何人可以访问、加密、解密或以其他方式获得公司的私人密码,否则任何人都应该被允许访问源的其余部分。
这是我的做法。我从 TikiWiki 复制了这种模式,它也这样做。
在某些通常包含密码的文件中,将它们设置为虚拟值,没关系。将其设置为您的客户应该看到的任何内容。在附近发表评论,让开发人员不要管这个文件并更改第二个文件。
在第二个文件中,如果它不存在则创建,输入实际密码。安排这个文件被第一个文件包含,导入,无论如何。
安排您的源代码管理忽略该文件。可能看起来像这样:
# in .gitignore
localsettings.py
# in settings.py
## Alter this value to log into the snack machine:
## developers: DON'T alter this, instead alter 'localsettings.py'
SECRET_VALUE = ""
try:
from localsettings import *
except:
pass
# in localsettings.py
SECRET_VALUE = "vi>emacs"
我已经构建了数据库用户名/密码对不属于代码删除的系统。关键是建立一个特定于站点的配置机制。然后你可以把这些信息放在有问题的盒子上,而不是代码库的一部分。
有一个好处:您不仅可以为不同的代码删除设置不同的密码,还可以为不同的开发人员设置不同的密码。:-)
你没有提到语言,所以这是我们使用的 vb.net 解决方案:
Imports System.Web.Security
Imports System.Security.Cryptography
Imports System.Text
Imports Microsoft.Win32
Public Class myCrypt
Private myKey As String = "somekeyhere"
Private cryptDES3 As New TripleDESCryptoServiceProvider()
Private cryptMD5Hash As New MD5CryptoServiceProvider()
Private Function Decrypt(ByVal myString As String) As String
cryptDES3.Key = cryptMD5Hash.ComputeHash(ASCIIEncoding.ASCII.GetBytes(myKey))
cryptDES3.Mode = CipherMode.ECB
Dim desdencrypt As ICryptoTransform = cryptDES3.CreateDecryptor()
Dim buff() As Byte = Convert.FromBase64String(myString)
Decrypt = ASCIIEncoding.ASCII.GetString(desdencrypt.TransformFinalBlock(buff, 0, buff.Length))
End Function
Private Function Encrypt(ByVal myString As String) As String
cryptDES3.Key = cryptMD5Hash.ComputeHash(ASCIIEncoding.ASCII.GetBytes(myKey))
cryptDES3.Mode = CipherMode.ECB
Dim desdencrypt As ICryptoTransform = cryptDES3.CreateEncryptor()
Dim MyASCIIEncoding = New ASCIIEncoding()
Dim buff() As Byte = ASCIIEncoding.ASCII.GetBytes(myString)
Encrypt = Convert.ToBase64String(desdencrypt.TransformFinalBlock(buff, 0, buff.Length))
End Function
End Class
以加密形式存储密码。编写一个自定义例程,在构建时解密密码并更新配置文件。这可以很容易地与 Ant 等构建工具集成。
如果在 C 中做,您可以存储为字符数组并将字符作为十进制引用。不确定这是否会破坏字符串,但可能有助于缓解一些问题。
char pass[]={72, 101, 108, 108, 111};