前提
事情开始是这样的,做项目的时候甲方使用fortify对我们项目的源代码进行的白盒扫描,其中有一个很常见的问题,是配置文件中的所有密码都是明文密码。于是目标就是修改配置文件的明文为密文。
情况
我个人因为之前没有对配置文件做过加密,但是知道有类似的功能,一番查找后选定了使用jasypt依赖进行加密。这个项目由于一些遗留问题,并不是使用maven管理的依赖。我看了一眼,发现相关的jar包已经有了。
jasypt-1.9.2.jar
jasypt-spring-boot-2.1.0.jar
jasypt-spring-boot-starter-2.1.0.jar
于是我再看了一遍配置文件,并没有使用了加密的配置,于是我开始按照网络上大部分为文章进行加密操作。
配置文件加密基本过程
- 首先引入jasypt的jar包,上述的三个jar包我都引入了
- 在main方法的app类中添加EnableEncryptableProperties注解
- 在application.yml中新增以下配置
jasypt:
encryptor:
password: secret
- 通过以下三种方法的任意一种方法来计算密文(一会我叙述一下为什么是三种方法)
- 通过命令行和jar包计算
# 加密
java -cp ./jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="123456" password=secret
# 解密
java -cp ./jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="miwen" password=secret
- 通过编写一个工具类使用BasicTextEncryptor计算加密
public class Main {
public static void main(String[] args) {
String password = "password"; // 这是用于加密/解密的密码
String text = "Hello World"; // 这是待加密的原始数据
// 初始化加密器
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword(password);
// 加密数据
String myEncryptedText = textEncryptor.encrypt(text);
System.out.println("myEncryptedText: " + myEncryptedText);
// 解密数据
String plainText = textEncryptor.decrypt(myEncryptedText);
System.out.println("plainText: " + plainText);
}
}
- 通过编写一个工具类使用StrongPasswordEncryptor计算加密
public class Main {
public static void main(String[] args) {
String password = "password"; // 这是用于加密/解密的密码
String text = "Hello World"; // 这是待加密的原始数据
// 初始化加密器
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm("PBEWithMD5AndDES");
encryptor.setConfig(config);
// 加密数据
String myEncryptedText = encryptor.encrypt(text);
System.out.println("myEncryptedText: " + myEncryptedText);
// 解密数据
String plainText = encryptor.decrypt(myEncryptedText);
System.out.println("plainText: " + plainText);
}
}
说明一下,通过资料查询,由于jasypt-spring-boot 3.0.3以上的版本和以下的版本默认的加密方式不同,在加密时一定要确认好版本。
上述的三种方式如果有更改加密方式的需求,请自行查询(PBEWithMD5AndDES)。例子中所有的密文我也不贴上去了,以miwen代替,需要的自己改改代码。
- 获取到密文后就可以在配置文件更换为密文了
spring:
datasource:
db:
password: ENC(miwen)
发现问题
ok,到此为止,所有的步骤已经结束了,按理说只要重启服务,就可以解决这个问题了。但问题就是,启动后直接给我抛出了一个错误
Failed to bind properties under ‘属性名‘ to java.lang.String
于是我开始百度,询问chatgpt,并查看报错信息,总结报错的原因就是:jasypt已经生效了,但是没有解析出对应的数据。网络上出现这个问题的原因大多都是因为版本前后的不同,导致的默认算法被改变,所以加密和解密的方式不同,才会无法解析。但问题是我根本没有使用高版本的jasypt啊。于是我死马当活马医,无论什么原因,先解决问题再分析问题。
修改方案包括但不限于以下几种情况:
- 修改配置文件的jasypt配置
# 这种情况就是因为版本前后的不同,导致的默认算法被改变,因为解密报错,所以无法解读配置参数值
jasypt:
encryptor:
password: secret
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
- 更换加密方式
上面的三种方式我都尝试过,甚至还验证了彼此的加密密文是否可以互相解密。
说明一下StrongPasswordEncryptor和BasicTextEncryptor的区别:
- BasicTextEncryptor: 它是一个基本使用的工具类,它提供了文本加密和解密的基本功能,适合于对安全性要求不是特别高的场景。它使用PBEWithMD5AndDES(Password-Based Encryption With MD5 And DES)算法进行加密和解密。
- StrongPasswordEncryptor: 它是一个提供更强大或更安全加密功能的工具类。它使用更强大的加密算法(SHA-256和随机盐)进行加密。如果应用程序需要处理一些敏感的信息,比如信用卡号、密码等可以使用这个方式。
- 修改版本
有些文章中说只要把jasypt更换到3.0.0就可以解决这个问题,虽然他们是从3.0.3回退的,但毕竟有则改之无则加勉,尝试一下也不是件坏事,结果就是仍然没有解决问题。
- 更换密钥和密文
更换密钥是因为在之前设置的密钥中有特殊字符,为了防止其他原因对加密解密的影响,于是就换了个只有数字和字母的密钥。至于更换密文,是因为每次加密时都会使用随机加盐算法,从而每次加密的密文不同。虽然密文不同,但是解密后的明文都是相同的。更换密文同样也是防止特殊字符对配置会有什么影响。
解决问题
我放弃了。我将注意力转到了其他的地方。我发现虽然所有密码都变成了密文,但是秘钥还是明文,于是我开始放弃对加密的执着,转而分析如何隐藏秘钥,就在我分析的时候,突然灵光一闪,发现了个重点:
在之前询问chatgpt的时候,他提了一嘴:秘钥是不是相同的。我确实也确认了一遍秘钥,是相同的。但是问题就在于:配置文件是有优先级的。写在代码里的值对应的优先级是最低的,所以我即使在代码中写死了,但是因为配置文件中也会存在相同的配置,就会导致代码内的默认值失效。
那么什么情况可以让配置文件中的秘钥失效呢?
于是我怀着激动地心打开了启动脚本 ./bin/start.sh
哪个傻逼把秘钥写进了项目的java启动命令中,害的老子看了五个小时,五!个!小!时!啊!
五个小时
我五个小时几乎啥都没干,你知道我这五个小时是怎么过的么,我还花了半个小时写文章骂你。