保护属性文件中的密码

我有一个连接到数据库的 java 应用程序。
数据库的用户名和密码存储在一个属性文件中。
如何避免在属性文件中以明文形式存储密码,同时仍然保留让用户更改密码的选项?
这里的主要动机是防止有人在管理员编辑属性文件时越过管理员的肩膀看到密码。
我读了 给你,有一个内置的方式来做到这一点,在 C # 。
了解 java,我不期望找到一个内置的解决方案,但我想听听其他人在做什么。
如果我没有找到任何好的选择,那么我可能会用一个常量密码来加密它,这个常量密码会保存在代码中。但我不想这么做,因为感觉不对。

2012年12月12日编辑 看起来没有魔法,我必须存储密码在代码或类似的东西。 最后,我们实现了一些非常类似于其中一个答案中提到的 Jasypt 的东西。 所以我接受 Jasypt 的答案,因为它是最接近明确答案的东西。

152083 次浏览

enter image description here

中加载、管理和透明地解密加密值提供了 Properties. EncryptableProperties类。属性文件,允许在同一文件中混合使用加密和未加密的值。

Http://www.jasypt.org/encrypting-configuration.html

通过使用 org.jasypt.properties. EncryptableProperties 对象,可以创建一个 应用程序将能够正确地读取和使用一个. properties 文件 像这样:

datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://localhost/reportsdb
datasource.username=reportsUser
datasource.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)

请注意 数据库密码是加密的(实际上,任何其他属性都可以 也要加密,不管是否与数据库配置有关)。

我们如何读取这个值? 像这样:

/*
* First, create (or ask some other component for) the adequate encryptor for
* decrypting the values in our .properties file.
*/
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("jasypt"); // could be got from web, env variable...
/*
* Create our EncryptableProperties object and load it the usual way.
*/
Properties props = new EncryptableProperties(encryptor);
props.load(new FileInputStream("/path/to/my/configuration.properties"));


/*
* To get a non-encrypted value, we just get it with getProperty...
*/
String datasourceUsername = props.getProperty("datasource.username");


/*
* ...and to get an encrypted value, we do exactly the same. Decryption will
* be transparently performed behind the scenes.
*/
String datasourcePassword = props.getProperty("datasource.password");


// From now on, datasourcePassword equals "reports_passwd"...

穷人的折衷解决方案是使用简单的多重签名方法。

例如,DBA 将应用程序数据库密码设置为50个字符的随机字符串。 TAKqWskc4ncvKaJTyDcgAHq82X7tX6GfK2fc386bmNw3muknjU

他或她给应用程序开发人员一半的密码,然后硬编码成 java 二进制文件。

Private String pass1 = “ TAKqWskc4ncvKaJTyDcgAHq82”

密码的另一半作为命令行参数传递。DBA 向系统支持人员或管理人员提供 pass2,这些人员或者输入应用程序启动时间,或者将其放入自动应用程序启动脚本。

Jar-pass2 X7tX6GfK2fc386bmNw3muknjU

当应用程序启动时,它使用 pass1 + pass2并连接到数据库。

这个解决方案有很多优点,但是没有提到的缺点。

您可以安全地将一半的密码放在命令行参数中,因为阅读它不会对您有很大帮助,除非您是拥有另一半密码的开发人员。

DBA 还可以更改密码的后半部分,开发人员不必重新部署应用程序。

源代码也可以是半公开的,因为读取它和密码不会让您的应用程序访问。

通过对数据库将接受的连接的 IP 地址范围添加限制,可以进一步改进这种情况。

如何提供自定义的 N- 因子身份验证机制?

在组合可用方法之前,假设我们可以执行以下操作:

1) Java 程序中的硬代码

2)存储在. properties 文件中

3)要求用户从命令行输入密码

4)要求用户从表单中输入密码

5)要求用户从命令行或表单加载密码文件

6)通过网络提供密码

7)许多选择(如画一个秘密,指纹,IP 特定的,等等等等)

第一个选项: 我们可以通过使用模糊处理使攻击者的事情变得更加复杂,但是这不被认为是一个好的对策。一个好的程序员可以很容易地理解它是如何工作的,如果他/她可以访问文件。我们甚至可以导出每个用户的二进制文件(或者只导出模糊部分或键部分) ,因此攻击者必须能够访问这个用户特定的文件,而不是另一个发行版本。 同样,我们应该找到一种方法来更改密码,例如通过重新编译或使用反射来实时更改类行为。

第二个选项: 我们可以将密码存储在。属性文件的加密格式,所以它不能直接从攻击者看到(就像 Jasypt一样)。如果我们需要一个密码管理器,我们也需要一个主密码,它也应该存储在某个地方-在一个。类文件、密钥存储库、内核、另一个文件甚至内存中的文件——都有它们各自的优缺点。
但是,现在用户只需编辑. properties 文件进行密码更改。

第三个选项: 在命令行运行时输入密码,例如 java -jar /myprogram.jar -p sdflhjkiweHIUHIU8976hyd

这不需要存储密码,并将保留在内存中。然而,history命令和操作系统日志可能是你最大的敌人。 要动态更改密码,您将需要实现一些方法(例如侦听控制台输入、 RMI、 socket、 REST bla bla bla) ,但是密码将始终保留在内存中。

人们甚至可以只在需要时临时解密它-> 然后删除解密,但始终保持加密密码在内存中。不幸的是,上述方法并没有提高对未经授权的内存访问的安全性,因为实现这一点的人很可能有权访问算法、 Salt 和所使用的任何其他秘密。

第4个选项: 提供自定义表单中的密码,而不是命令行中的密码。这将避免日志曝光的问题。

第五个选项: 提供一个文件作为密码,以前存储在另一个媒介上-> 然后硬删除文件。这将再次规避日志曝光的问题,加上不需要打字,可能是肩膀冲浪被盗。当需要更改时,提供另一个文件,然后再次删除。

第六个选项: 再次避免肩膀冲浪,一个可以实现 RMI 方法调用,提供密码(通过加密通道)从另一个设备,例如通过移动电话。但是,您现在需要保护您的网络通道和对其他设备的访问。

我将选择上述方法的组合来实现最大的安全性,因此必须访问。类文件,属性文件,日志,网络频道,肩上冲浪,男人在中间,其他文件等等等等。这可以很容易地使用所有子密码之间的 XOR 操作来生成实际的密码。

然而,我们不能被保护免受未经授权的内存访问,这只能通过使用一些访问限制的硬件(如智能卡,HSM,SGX)来实现,在这些硬件中,所有的东西都被计算进去,没有任何人,甚至合法的所有者能够访问解密密钥或算法。同样,一个人也可以窃取这个硬件,有报告的 侧道攻击侧道攻击侧道攻击,可以帮助攻击者在关键字提取,在某些情况下,你需要信任另一方(例如与新交所,你信任英特尔)。当然,当安全飞地克隆(反汇编)成为可能时,情况可能会恶化,但我想这需要几年时间才能实现。

此外,可以考虑一种密钥共享解决方案,其中将完整的密钥在不同的服务器之间进行分割。然而,重建后,完整的钥匙可以被偷走。减轻上述问题的唯一方法是使用 安全的多方计算

我们应该始终记住,无论输入方法是什么,我们都需要确保不会受到网络嗅探(MITM 攻击)和/或键盘记录器的攻击。

事实上,这是 在配置文件中加密密码? ?的复制品。

到目前为止,我找到的最好的解决方案是这个答案: https://stackoverflow.com/a/1133815/1549977

优点: 密码保存为字符数组,而不是字符串。还是不太好,但比其他任何东西都好。