前言

前几天,有个前同事向我吐槽,他们公司有个大神把公司的项目代码全部上传到了 github,并且是公开项目,所有人都可以浏览。更加恐怖的是项目里面包含配置文件,数据库信息、redis 配置、各种公钥私钥密码全在项目里面,也一同上传了。

如果只是单纯的业务代码泄露,情况倒还好,因为别人知道你代码,要想搞你,他必须要把源码看一遍,分析漏洞。 又因为代码上线,经过了层层测试,漏洞也不好找,至少短时间内不好找。但是别人拿到你的数据库信息,那就开启了上帝模式,想怎么玩就怎么玩,删库都不用跑路的。

不过,还好他们发现的及时,第一时间删除了 github 上的项目,但是不能保证当时的项目没有人拉到本地,所以第二就是把配置文件内的各项配置都更改一遍,改配置听起来简单,但是要知道有些配置是不能热更新的。很多配置要把前一个配置修改后才能使用,新老配置不能共存,你改的瞬间运行的项目就崩了,必须要停机维护才可以。为了变更配置他们花了大量的人力物力与精力。

其实这种惨痛的教训本可以避免的,防止配置泄露,通用的有两种形式。一种是使用配置中心,本地不保存配置,启动的时候从配置中心获取,这应该是最优解了。但是很多时候你所做的项目并没有使用配置中心,配置就在项目里面裸奔。这个时候就需要本地加密的形式防止配置泄露了,常用框架是 jasypt。同时它也是本文的主题,话不多说,直接开始,看看如果使用 jasypt 进行配置加密。

依赖

pom.xml

<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.1</version>
</dependency> <build>
<plugins>
<plugin>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-maven-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>

说明

如果你使用了 spring boot 那么使用 jasypt 很简单,只要依赖一个 jasypt-spring-boot-starter 包就可以了。

至于 jasypt-maven-plugin 是方便我们加密解密配置的 maven 插件,后面会说用法。

配置

application.properties

my.conf.test1=123
my.conf.test2=DEC(123)
# 记得看最佳实践
jasypt.encryptor.password=lE1rl5K$

说明

总共有三个配置,第一个配置 my.conf.test1 是不需要加密的配置,第二个配置 my.conf.test2 是需要加密的配置,要加密的内容是 123。注意他的格式的是 DEC(待加密内容)。第三个 jasypt.encryptor.password  配置是我们的加密私钥,默认使用的加密算法是 PBEWITHHMACSHA512ANDAES_256 ,这个密钥可以是任意字符串,而   lE1rl5K$ 只是我随机生成的,你可以自由发挥。

生成加密内容

好了,到目前为止,我们的配置还是明文的。my.conf.test2 是我们想加密的配置,他与 my.conf.test1 唯一的区别就是多了一个 DEC() 包裹,这算哪门子加密,其实我们还差一步。还记得我们上面加依赖的时候,配置了一个 Maven 插件吗?现在就是用到他的时候,在我们的项目目录路径下执行如下命令:

命令

 mvn jasypt:encrypt -Djasypt.encryptor.password="lE1rl5K$"

注意在执行的时候,password 要换成你自己在上文配置的密钥。执行完后,看到终端输出了一大堆日志,然后就没有然后了。但是真的是这样吗?

你再打开 application.properties 看一下,有什么不一样的地方。

my.conf.test1=123
my.conf.test2=ENC(0ZWzuD2DH0BZ8ANGMZxQyC6wv84sQLJtE6u7bcRjU+DntbMgkBvE2Z4fSzKKhYN8)
jasypt.encryptor.password=lE1rl5K$

我们发现,三个配置中其它两个是原来的样子,但是 my.conf.test2 变了,首先格式从之前 DEC(xxx) 变成了 ENC(xxx) 。另外括号的 123 变成了 0ZWzuD2DH0BZ8ANGMZxQyC6wv84sQLJtE6u7bcRjU+DntbMgkBvE2Z4fSzKKhYN8

这其实就是配置加密后的样子。这条命令的功能其实很简单:

  1. 从配置文件中加载配置
  2. 从配置中找到有 DEC(xxx) 格式并且不是 jasypt 开头的配置
  3. 使用配置的密钥加密并覆盖配置为 ENC(加密后的值)

另外通过插件也可以解密,使用

mvn jasypt:decrypt -Djasypt.encryptor.password="lE1rl5K$"

执行这条命令会反过来,把 ENC(xxx) 内容的配置解密成 DEC(明文) 打印在控制台,注意是控制台,而不是把配置文件变回去,作者说这样是为了安全。

验证

TestController.java

/*
*
* * *
* * * blog.coder4j.cn
* * * Copyright (C) 2016-2019 All Rights Reserved.
* *
*
*/
package cn.coder4j.study.example.jasypt; import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; /**
* @author buhao
* @version TestController.java, v 0.1 2019-12-26 10:55 buhao
*/
@RestController
@RequestMapping("/test")
public class TestController { @Value("${my.conf.test1}")
private String confTest1;
@Value("${my.conf.test2}")
private String confTest2; @GetMapping("/getConf/{type}")
@ResponseBody
public Object getConfTest(@PathVariable Integer type) {
if (type == 1) {
return confTest1;
} else {
return confTest2;
}
}
}

说明

代码其实很简单,首先通过 @Value 的方式读取配置,同时把没有加密的配置与加密的配置都读出来,然后通过接口,当路径参数为 1 的时候返回没有经过加密的参数,当路径参数为 2 的时候返回加密过的参数。要是都返回 123 说明我们成功了。

为了方便验证,直接用 IDEA 的内置工具,下面是验证结果:

未加密的参数

经过加密的参数

结果如我们所料,加密成功。

获取配置的大致流程其中跟上面加密配置的流程大致反过来:

  1. 拦截获取配置的操作
  2. 如果拦截到的配置是 ENC(xxx) 格式
  3. 读取 jasypt.encryptor.password 密钥
  4. 通过密钥解密配置

最佳实践

密钥与配置分开保存

可以看到,通过 jasypt 十分的方便,第一依赖,第二配置,其中配置除加密内容外还有一个 jasypt.encryptor.password 。这个前文也说了是用于加密与解密的密码,通过它可以加解密配置。

回到开头,我们加密的目的是为了防止代码泄露的时候把配置一起给泄露出去了。配置是没有问题了,我们加密了,但是我们同时把密钥也放在配置文件中了。这相当于什么呢?就像你把门给锁了,但是钥匙还插在锁上。

所以密钥一定要跟配置分开保存,通常是通过启动命令传给应用,比如下面这种:

java -Djasypt.encryptor.password="password" -jar my-application.jar

如果再保险一点,可以把密钥放在环境变量中,再通过命令传给应用。

非对称加密

默认使用的加密算法为对称加密 HHMAC ,既然有对称那肯定也有非对称。

这里的对称与非对称指的是密钥的保存方式,对称加密是指的是加密与解密共用一个密钥,也就是说我用这个密钥即可以用来加密也可以用来解密。上一条说为了安全我们要把配置跟密钥分开保存,一般保存在两个地方,一个是线上服务器,一个是项目负责人的电脑上了,因为他要把配置从明文变成密文。为什么是项目负责人的电脑上,因为密钥不可能人手一份,那样又会增大泄露风险。

但是这样的话又会出来一种问题,一个项目涉及了太多配置,我加一个配置找下项目负责人帮我生成个密文,加一个生成一个,项目负责人变成工具人了。

这个时候我们可以通过非对称加密的方式来解决,这种方式的好处就是有一对密码,分别称为公钥与私钥,公钥用来生成加密数据,可以放心大胆人手一份,而私钥放在服务器上进行运行时候的解密工作,因篇幅有限,具体使用方式可以通过文末的链接查看官方文档。

环境隔离

配置肯定是区分环境的,有些环境安全等级没有那么高,比如开发与测试环境,没有必要加密。而预发及生产环境就需要加密,并且推荐使用不同的密钥,这样最大程度的避免安全问题。

其实相关

  1. jasypt-spring-boot (jasypt 的 github 地址,有着详尽的文档)
  2. 本文 DEMO (因为文章篇幅有限,只展示部分代码,具体代码已上传 github)

【Jasypt】给你的配置加把锁的更多相关文章

  1. 为您的Office文档加把锁-ADRMS的安装

    为您的Office文档加把锁-ADRMS的安装 如今不少企业越来越重视自己KM(知识管理系统)的建立对于KM的建立实施虽然可以有效地解决企业在知识管理上的问题对于一些具有商业利益关系的机密文件(例如: ...

  2. Spring Cloud Nacos实现动态配置加载的源码分析

    理解了上述Environment的基本原理后,如何从远程服务器上加载配置到Spring的Environment中. NacosPropertySourceLocator 顺着前面的分析思路,我们很自然 ...

  3. 关于flume配置加载(二)

    为什么翻flume的代码,一方面是确实遇到了问题,另一方面是想翻一下flume的源码,看看有什么收获,现在收获还谈不上,因为要继续总结.不够已经够解决问题了,而且确实有好的代码,后续会继续慢慢分享,这 ...

  4. 关于flume配置加载

    最近项目在用到flume,因此翻了下flume的代码, 启动脚本: nohup bin/flume-ng agent -n tsdbflume -c conf -f conf/配置文件.conf -D ...

  5. Java实现配置加载机制

    前言 现如今几乎大多数Java应用,例如我们耳熟能详的tomcat, struts2, netty…等等数都数不过来的软件,要满足通用性,都会提供配置文件供使用者定制功能. 甚至有一些例如Netty这 ...

  6. mysql 在线加索引 锁表

    mysql在线修改表结构大数据表的风险与解决办法归纳 - 王滔 - 博客园 http://www.cnblogs.com/wangtao_20/p/3504395.html MySQL 加索引 加字段 ...

  7. 异常处理之IIS配置加载出错

    问题详情:  一台部署在海外服务器,在管理IIS过程中,出现问题 There was an error when trying to connect. Do you want > to rety ...

  8. 深入理解 Laravel 中 config 配置加载原理

    Laravel的配置加载其实就是加载config目录下所有文件配置.如何过使用php artisan config:cache则会把加载的配置合并到一个配置文件中,下次请求就不会再去加载config目 ...

  9. Springboot学习01- 配置文件加载优先顺序和本地配置加载

    Springboot学习01-配置文件加载优先顺序和本地配置加载 1-项目内部配置文件加载优先顺序 spring boot 启动会扫描以下位置的application.properties或者appl ...

随机推荐

  1. Linux(Centos)安装图形化界面步骤

    最近有重新来捣鼓捣鼓Linux了,这次撸的版本是centos7.4的,虽然说是不要桌面,但是感觉还是安装一下比较好balalalala.........废话不说的直接进入正题: 安装X 首先安装X(X ...

  2. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 排版:地址(Address)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  3. 最长公共子序列/子串 LCS(模板)

    首先区分子序列和子串,序列不要求连续性(连续和不连续都可以),但子串一定是连续的 1.最长公共子序列 1.最长公共子序列问题有最优子结构,这个问题可以分解称为更小的问题 2.同时,子问题的解释可以被重 ...

  4. 什么是Socket:

    先了解一些前提: 网络由下往上分为 物理层 .数据链路层 . 网络层 . 传输层 . 会话层 . 表现层 和 应用层.通过初步了解,我知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对 ...

  5. redis 高性能的原因

    1. redis 数据存储在内存中: 2. redis 是单线程: 3. redis 多路复用: 指令先放到队列里 4.redis 使用resp 协议

  6. 第5节 Actor实战:1 - 6

    10.3.   Actor实战 10.3.1.    第一个例子 怎么实现actor并发编程: 1.定义一个class或者是object继承Actor特质,注意导包import scala.actor ...

  7. (未解决)flume监控目录,抓取文件内容推送给kafka,报错

    flume监控目录,抓取文件内容推送给kafka,报错: /export/datas/destFile/220104_YT1013_8c5f13f33c299316c6720cc51f94f7a0_2 ...

  8. Linux centos7VMware Apache和PHP结合、Apache默认虚拟主机

    一.Apache和PHP结合 httpd主配置文件/usr/local/apache2.4/conf/httpd.conf 启动报错 [root@davery ~]# /usr/local/apach ...

  9. intelliJ IDEA 全屏键盘手

    从MyEclipse到IntelliJ IDEA --让你脱键盘,全键盘操作 从MyEclipse转战到IntelliJ IDEA的经历 我一个朋友写了一篇"从Eclipse到Android ...

  10. Python之第一次自夸

    有一个好玩的代码 import win32com.client g = win32com.client.Dispatch("SAPI.SPVOICE") g.Speak(" ...