前言

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

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

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

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

依赖

pom.xml

  1. <dependency>
  2. <groupId>com.github.ulisesbocchio</groupId>
  3. <artifactId>jasypt-spring-boot-starter</artifactId>
  4. <version>3.0.1</version>
  5. </dependency>
  6. <build>
  7. <plugins>
  8. <plugin>
  9. <groupId>com.github.ulisesbocchio</groupId>
  10. <artifactId>jasypt-maven-plugin</artifactId>
  11. <version>3.0.0</version>
  12. </plugin>
  13. </plugins>
  14. </build>

说明

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

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

配置

application.properties

  1. my.conf.test1=123
  2. my.conf.test2=DEC(123)
  3. # 记得看最佳实践
  4. 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 插件吗?现在就是用到他的时候,在我们的项目目录路径下执行如下命令:

命令

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

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

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

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

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

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

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

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

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

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

验证

TestController.java

  1. /*
  2. *
  3. * * *
  4. * * * blog.coder4j.cn
  5. * * * Copyright (C) 2016-2019 All Rights Reserved.
  6. * *
  7. *
  8. */
  9. package cn.coder4j.study.example.jasypt;
  10. import org.springframework.beans.factory.annotation.Value;
  11. import org.springframework.web.bind.annotation.GetMapping;
  12. import org.springframework.web.bind.annotation.PathVariable;
  13. import org.springframework.web.bind.annotation.RequestMapping;
  14. import org.springframework.web.bind.annotation.ResponseBody;
  15. import org.springframework.web.bind.annotation.RestController;
  16. /**
  17. * @author buhao
  18. * @version TestController.java, v 0.1 2019-12-26 10:55 buhao
  19. */
  20. @RestController
  21. @RequestMapping("/test")
  22. public class TestController {
  23. @Value("${my.conf.test1}")
  24. private String confTest1;
  25. @Value("${my.conf.test2}")
  26. private String confTest2;
  27. @GetMapping("/getConf/{type}")
  28. @ResponseBody
  29. public Object getConfTest(@PathVariable Integer type) {
  30. if (type == 1) {
  31. return confTest1;
  32. } else {
  33. return confTest2;
  34. }
  35. }
  36. }

说明

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

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

未加密的参数

经过加密的参数

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

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

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

最佳实践

密钥与配置分开保存

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

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

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

  1. 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. DVWA靶机-sql自动注入

    1. 使用dvwa靶机进行sql注入实战(注:当前靶机安全级别为low) 打开sql漏洞,发现输入不同的数字会返回不同的信息, 先尝试手工判断是否存在sql注入 一般sql注入语句像这样,我们构造的是 ...

  2. 都客仿站高手已注册旗舰版V3.1

    链接:https://pan.baidu.com/s/1R5ldFDjekuXmEp42-8SQSQ 提取码:gkm9

  3. javascript ----一些边距知识

    Style top 属性  Style 对象 定义和用法 top 属性设置或返回定位元素的顶部位置. 该属性规定了元素的顶部位置,包括:内边距.滚动条.边框和外边距. 提示:一个定位元素是元素的 po ...

  4. String_Java

    1.substring() 方法返回字符串的子字符串. 语法 public String substring(int beginIndex)//返回第beginIndex个字符以后的子字符串 或 pu ...

  5. XModem与YModem

    XModem用在串口异步传文件: #define SOH 0x01 #define STX 0x02 #define EOT 0x04 #define ACK 0x06 #define NAK 0x1 ...

  6. js中的||和&&的用法

    与其他语言不同,在JS中,a&&b或者a||b返回的是要么是a,要么是b:而其他语言中返回的是true or false 对于js中的或与运算,需要隐式的转换为boolean类型再来运 ...

  7. CS231n -Assignments 1 Q1 and Q2

    前言 最近在youtube 上学习CS231n的课程,并尝试完成Assgnments,收获很多,这里记录下过程和结果以及过程中遇到的问题,我并不是只是完成需要补充的代码段,对于自己不熟悉的没用过的库函 ...

  8. 「JLOI2011」飞行路线

    前言 看到好多大佬都在跑分层图最短路,\(\text{DP}\) 解法的我瑟瑟发抖... 题目描述 给定一张 \(N\) 个点(点编号从 \(0\) 到 \(N-1\)),\(M\) 条边的无向带权图 ...

  9. 「学习笔记」Treap

    「学习笔记」Treap 前言 什么是 Treap ? 二叉搜索树 (Binary Search Tree/Binary Sort Tree/BST) 基础定义 查找元素 插入元素 删除元素 查找后继 ...

  10. openstack的一台Nova主机上的虚拟机网络的配置

    1.一台虚拟机器的网络配置,通过openstack/nova计算节点服务生成的虚拟机配置文件 <interface type='bridge'> <mac address='fa:1 ...