"打铁还需自身硬",JRASP在防护业务安全的同时,也十分重视自身安全性建设。JRASP的防护策略(包括hook类、参数检测逻辑等) 仍然一定程度上于依赖信息不对称,如何保障策略在传输过程、运行时不被恶意窃取分析是一个重要问题。 JRASP对整个通信链路进行了加密,包括:
管理端下发给守护进程的的安全策略加密;
守护进程与agent通信的socket加密,防止插件被控制;
插件jar包传输全过程加密,在类加载时解密,最大程度防止jar包被反编译;
上面的第1、2点易实现,技术难点不高,因此本文将介绍第三点运行时类加密解密技术。
1.编译时加密一般原理
下面为java 对称加密基本实现代码。对于一个编译好的 class 文件,调用加密算法,将class 文件转为密文即可。
/**
* AES 加密
*
* @param content 待加密内容
* @param aesKey 密码
* @return
*/
public static byte[] encrypt(byte[] content, String aesKey) {
if (aesKey == null || aesKey.length() != 16) {
return null;
}
try {
byte[] bytes = aesKey.getBytes("UTF-8");
SecretKeySpec skeySpec = new SecretKeySpec(bytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(content);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
jrasp安全插件以 jar 包形式输出,以 file-hook 模块为例子,将 jar 解压后,com.jrasp.agent.module 包下的类需要加密,目标加密文件是FileHook.class (jar包依赖的三方包,如工具类,可以不加密)。
2.编译加密maven plugin 插件
为了更友好的将加密逻辑嵌入打包编译流程中,避免繁琐的jar包解压、加密class文件、压缩jar包等流程。jrasp 官方将加密逻辑写入到了maven plugin 插件中,插件代码在jrasp-agent 项目下的 jrasp-encrypt 工程中。并且无需其他配置,仅需要在 bin/DECRYPT_KEY.txt 中配置 加密/解密 密钥即可,简化操作。
jrasp-module 工程引入加密插件,并且配置插件参数配置:密钥文件路径和需要加密包(多个包,逗号分隔)
3.运行时解密
jrasp 采用自定义类加载器加载jar包,在类加载器的loadClass环节(类加载器的该方法作用可以参考其他博文)(class 文件流转换为 class 对象过程),将加密后二进制文件解密为jvm可以识别的class文件。
class 解密的技术实现细节:
4.加密算法与密钥保存分发
1.加密算法的选择:
由于 jrasp 要兼容不同的jdk版本,不同jdk版本加密算法差异较大,兼容性存在很大问题,考虑兼容性,这里 jrasp 采用了对称加密。(如果业务指为特定jdk如openjdk,可以采用该jdk支持的非对称加密)。
2.密钥的保存:
为了保障加密的安全性,密钥理论上是不允许明文配置在conf文件中(配置文件密钥容易泄漏)。密钥jrasp-daemon 编译时固化到可执行文件中。并且密钥与jrasp-daemon 可执行文件的md5一一对应,保证无法篡改。
3.密钥的分发给:
jrasp-daemon 通过attach 机制动态传递给 java agent。
5.总结
jrasp 目前是业内唯一采用运行时加密算法保障安全策略的rasp。安全策略jar包磁盘加密,运行时解密,密钥不落盘。大大提高了rasp自身的安全性。